本做法是基于暴力枚举第一行走法的O((n^2)*(2^n))的方法的预处理优化版。
首先做一个预处理,对于空白的棋盘,枚举第一行的走法,然后以把前n-1行复原成白的为目标,逐行向下推出下面n-1行的走法,记录下最后第n行的状态和对应走法的映射(可以用邻接表)。
对于每组case,第一行不动,同样地,以把前n-1行复原成白的为目标,逐行向下推出下面n-1行的走法x,这时第n行残留下一个状态,找到该状态所映射到的预处理得到的走法y(y可能不唯一),将x与y叠加(取异或)就得到一个可行解,然后就是统计可行解中1的个数,再取最小值输出就好了。
预处理的复杂度为O((n^3)*(2^n)),单组case复杂度为O(n^2)。在OJ上跑有点卡常数,我在统计1的个数那里加了个猥琐的常优才能过,不知道还有什么好方法能快速统计二进制数中1的个数。
// 1743 aroslhy(Aros) Accepted 2424ms 1724KB GPP 1930 B 2013-05-19 12:51
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 8, MAXN = MAX+5, MAXM = 1<<(MAX+1);
int T, N, e, head[MAXN][1<<MAX], next[MAXM], cnt[1<<16];
unsigned long long a[MAXN], v[MAXM];
void Addedge(int n, int t, unsigned long long mov)
{
v[e] = mov;
next[e] = head[n][t]; head[n][t] = e++;
}
void Select(int N, unsigned long long &mat, unsigned long long &mov, int x, int y)
{
mov ^= 1ULL<<(N*x+y);
mat ^= 1ULL<<(N*x+y);
if (x > 0)
mat ^= 1ULL<<(N*(x-1)+y);
if (x+1 < N)
mat ^= 1ULL<<(N*(x+1)+y);
if (y > 0)
mat ^= 1ULL<<(N*x+y-1);
if (y+1 < N)
mat ^= 1ULL<<(N*x+y+1);
}
int Count(unsigned long long x)
{
int pat = (1<<16)-1, res = 0;
for (int i = 0; i < 4; i++)
res += cnt[(x>>(i*16))&pat];
return res;
}
int main()
{
memset(head, -1, sizeof(head));
for (int n = 1; n <= 8; n++)
{
int tot = 1<<n;
for (int s = 0; s < tot; s++)
{
unsigned long long mat = 0, mov = 0;
for (int j = 0; j < n; j++) if (s&(1<<j))
Select(n, mat, mov, 0, j);
for (int i = 1; i < n; i++)
for (int j = 0; j < n; j++) if (mat&(1ULL<<(n*(i-1)+j)))
Select(n, mat, mov, i, j);
int t = mat>>((n-1)*n);
Addedge(n, t, mov);
}
}
int tot = 1<<16;
for (int i = 0; i < tot; i++)
for (int j = 0; j < 16; j++) if (i&(1<<j))
cnt[i]++;
scanf("%d", &T);
while (T--)
{
scanf("%d", &N);
unsigned long long mat = 0, mov = 0;
for (int i = 0; i < N; i++)
{
scanf("%llu", &a[i]);
for (int j = 0; j < N; j++)
mat ^= ((a[i]>>(N-j-1))&1)<<(N*i+j);
}
for (int i = 1; i < N; i++)
for (int j = 0; j < N; j++) if (mat&(1ULL<<(N*(i-1)+j)))
Select(N, mat, mov, i, j);
int fin = mat>>((N-1)*N), ans = -1;
for (int i = head[N][fin]; i != -1; i = next[i])
ans = (ans != -1) ? min(ans, Count(mov^v[i])) : Count(mov^v[i]);
printf("%d\n", ans);
}
return 0;
}