一名苦逼的OIer,想成为ACMer

Iowa_Battleship

POJ3254或洛谷1879 Corn Fields

一道状压\(DP\)

POJ原题链接

洛谷原题链接

很显然的状压,\(1\)表示种植,\(0\)表示荒废。
将输入直接进行状压,而要满足分配的草场是适合种草的土地,即是分配时的状态中的\(1\),在输入对应的状态中也是\(1\),而\(0\)对应\(0,1\)都可以。
设输入的这行状态为\(a[i]\),分配时的这行状态为\(x\),则用位运算来表示上述条件即\((\sim a[i])\&x=0\),设其为函数\(judge(i,x)\)
因为每个草场不能相邻,对于每一行,可以先预处理出在二进制下没有\(1\)是相邻的所以数,记录在集合\(S\)中,集合大小为\(l\);对于列上要求不相邻,即上下两行不能有\(1\)相邻,即\(i\&(i-1)=0\)
定义\(f[i][j]\)表示分配至第\(i\)行,第\(i\)行的状态为\(j\)

\(\qquad\qquad f[i][j]=\sum\limits_{k=0,judge(i-1,S[k])\text{且}i\&(i-1)=0}^{l-1}f[i-1][k],\text{其中}i,j\text{满足}judge(i,S[j])\)

最终结果即是\(\sum\limits_{i=0}^{l-1}f[n][i]\)
另外,在\(DP\)时记得对\(10^9\)进行取模。

#include<cstdio>
using namespace std;
const int N = 13;
const int M = 400;
const int mod = 1e9;
int f[N][M], a[N], S[M];
int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c<'0' || c>'9'; c = getchar())
		p = (c == '-' || p) ? 1 : 0;
	for (; c >= '0'&&c <= '9'; c = getchar())
		x = x * 10 + (c - '0');
	return p ? -x : x;
}
bool judge(int x, int y)
{
	if ((~a[x]) & y)
		return false;
	return true;
}
int main()
{
	int i, j, n, m, l = 0, k, o;
	n = re();
	m = re();
	o = 1 << m;
	for (i = 1; i <= n; i++)
		for (j = 1; j <= m; j++)
			a[i] |= (re() << (m - j));
	for (i = 0; i < o; i++)
		if (!(i&(i << 1)))
			k = S[l++] = i;
	f[0][0] = 1;
	for (i = 1; i <= n; i++)
		for (j = 0; j < l; j++)
			if (judge(i, S[j]))
				for (k = 0; k < l; k++)
					if (judge(i - 1, S[k]) && !(S[j] & S[k]))
						(f[i][j] += f[i - 1][k]) %= mod;
	for (k = i = 0; i < l; i++)
		(k += f[n][i]) %= mod;
	printf("%d", k);
	return 0;
}

posted on 2018-08-20 13:13  Iowa_Battleship  阅读(116)  评论(0编辑  收藏  举报

导航