一道状压(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)。
(qquadqquad f[i][j]=sumlimits_{k=0,judge(i-1,S[k]) ext{且}i&(i-1)=0}^{l-1}f[i-1][k], ext{其中}i,j ext{满足}judge(i,S[j]))
最终结果即是(sumlimits_{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;
}