NJUST 1743 Boring Game(2013南京邀请赛G题)(thx to _LT_zyc)

  本做法是基于暴力枚举第一行走法的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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值