C ++ 11, 28
Điều này cũng sử dụng một phương pháp lập trình động dựa trên các hàng. Phải mất 28 giây để chạy với đối số 13 cho tôi. Thủ thuật yêu thích của tôi là next
chức năng sử dụng một số bashing để tìm cách sắp xếp hàng theo từ vựng tiếp theo thỏa mãn mặt nạ và quy tắc không có 3 hàng liên tiếp.
Hướng dẫn
- Cài đặt MinGW-w64 mới nhất với các luồng SEH và Posix
- Biên dịch chương trình với
g++ -std=c++11 -march=native -O3 <filename>.cpp -o <executable name>
- Chạy với
<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;
}