C ++ 11, 28s
Esto también utiliza un enfoque de programación dinámica basado en filas. Me tomó 28 segundos correr con el argumento 13 para mí. Mi truco favorito es la next
función que utiliza un poco de bashing para encontrar la disposición lexicográfica de la siguiente fila que satisface una máscara y la regla de no 3 en fila.
Instrucciones
- Instale el último MinGW-w64 con hilos SEH y Posix
- Compila el programa con
g++ -std=c++11 -march=native -O3 <filename>.cpp -o <executable name>
- Corre con
<executable name> <n>
#include <vector>
#include <stddef.h>
#include <iostream>
#include <string>
#ifdef _MSC_VER
#include <intrin.h>
#define popcount32 _mm_popcnt_u32
#else
#define popcount32 __builtin_popcount
#endif
using std::vector;
using row = uint32_t;
using xcount = uint8_t;
uint16_t rev16(uint16_t x) { // slow
static const uint8_t revbyte[] {0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255};
return uint16_t(revbyte[x >> 8]) | uint16_t(revbyte[x & 0xFF]) << 8;
}
// returns the next number after r that does not overlap the mask or have three 1's in a row
row next(row r, uint32_t m) {
m |= r >> 1 & r >> 2;
uint32_t x = (r | m) + 1;
uint32_t carry = x & -x;
return (r | carry) & -carry;
}
template<typename T, typename U> void maxequals(T& m, U v) {
if (v > m)
m = v;
}
struct tictac {
const int n;
vector<row> rows;
size_t nonpal, nrows_c;
vector<int> irow;
vector<row> revrows;
tictac(int n) : n(n) { }
row reverse(row r) {
return rev16(r) >> (16 - n);
}
vector<int> sols_1row() {
vector<int> v(1 << n);
for (uint32_t m = 0; !(m >> n); m++) {
auto m2 = m;
int n0 = 0;
int score = 0;
for (int i = n; i--; m2 >>= 1) {
if (m2 & 1) {
n0 = 0;
} else {
if (++n0 % 3)
score++;
}
}
v[m] = score;
}
return v;
}
void gen_rows() {
vector<row> pals;
for (row r = 0; !(r >> n); r = next(r, 0)) {
row rrev = reverse(r);
if (r < rrev) {
rows.push_back(r);
} else if (r == rrev) {
pals.push_back(r);
}
}
nonpal = rows.size();
for (row r : pals) {
rows.push_back(r);
}
nrows_c = rows.size();
for (int i = 0; i < nonpal; i++) {
rows.push_back(reverse(rows[i]));
}
irow.resize(1 << n);
for (int i = 0; i < rows.size(); i++) {
irow[rows[i]] = i;
}
revrows.resize(1 << n);
for (row r = 0; !(r >> n); r++) {
revrows[r] = reverse(r);
}
}
// find banned locations for 1's given 2 above rows
uint32_t mask(row a, row b) {
return ((a & b) | (a >> 1 & b) >> 1 | (a << 1 & b) << 1) /*& ((1 << n) - 1)*/;
}
int calc() {
if (n < 3) {
return n * n;
}
gen_rows();
int tdim = n < 5 ? n : (n + 3) / 2;
size_t nrows = rows.size();
xcount* t = new xcount[2 * nrows * nrows_c]{};
#define tb(nr, i, j) t[nrows * (nrows_c * ((nr) & 1) + (i)) + (j)]
// find optimal solutions given 2 rows for n x k grids where 3 <= k <= ceil(n/2) + 1
{
auto s1 = sols_1row();
for (int i = 0; i < nrows_c; i++) {
row a = rows[i];
for (int j = 0; j < nrows; j++) {
row b = rows[j];
uint32_t m = mask(b, a) & ~(1 << n);
tb(3, i, j) = s1[m] + popcount32(a << 16 | b);
}
}
}
for (int r = 4; r <= tdim; r++) {
for (int i = 0; i < nrows_c; i++) {
row a = rows[i];
for (int j = 0; j < nrows; j++) {
row b = rows[j];
bool rev = j >= nrows_c;
auto cj = rev ? j - nrows_c : j;
uint32_t m = mask(a, b);
for (row c = 0; !(c >> n); c = next(c, m)) {
row cc = rev ? revrows[c] : c;
int count = tb(r - 1, i, j) + popcount32(c);
maxequals(tb(r, cj, irow[cc]), count);
}
}
}
}
int ans = 0;
if (tdim == n) { // small sizes
for (int i = 0; i < nrows_c; i++) {
for (int j = 0; j < nrows; j++) {
maxequals(ans, tb(n, i, j));
}
}
} else {
int tdim2 = n + 2 - tdim;
// get final answer by joining two halves' solutions down the middle
for (int i = 0; i < nrows_c; i++) {
int apc = popcount32(rows[i]);
for (int j = 0; j < nrows; j++) {
row b = rows[j];
int top = tb(tdim2, i, j);
int bottom = j < nrows_c ? tb(tdim, j, i) : tb(tdim, j - nrows_c, i < nonpal ? i + nrows_c : i);
maxequals(ans, top + bottom - apc - popcount32(b));
}
}
}
delete[] t;
return ans;
}
};
int main(int argc, char** argv) {
int n;
if (argc < 2 || (n = std::stoi(argv[1])) < 0 || n > 16) {
return 1;
}
std::cout << tictac{ n }.calc() << '\n';
return 0;
}