POJ 3254 Corn Fields(状压DP)

时间:2024-11-20 19:34:25
Corn Fields
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 13732   Accepted: 7216

Description

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input

Line 1: Two space-separated integers: M and N 
Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

Sample Input

2 3
1 1 1
0 1 0

Sample Output

9

Hint

Number the squares as follows:
1 2 3
  4  

There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.

题目链接:POJ 3254

做的第二道状态压缩DP题,按自己前一道的思路写的,写的比较慢但是过程感觉很清晰,修改了很多次,最后0MS过了还是不错的

这题跟做过的其他入门题有一点不同,就是他不仅要不相邻(可以同一列),因此第$i$行怎么放不影响第$i-2$,$i+2$行,因此就不需要用或进行状态叠加,显然状压基本法第一步就是初始化,把第一行的数据初始化,

所以为了方便和快速遍历所有本身不相邻的状态,先预处理把状态存到fit里,有tot个,然后枚举每一个状态看是否能放到这一行草地上,我就偷个懒直接用bitset for一遍判断——若当前不是草地但状态里却有一个牛显然这就不合法不能放进草地,否则$dp[0][当前十进制状态]=1$。

然后按照基本步骤先遍历每一行,再枚举上一行$k$和这一行的状态$j$,若上一行$j$可以放草地且这一行$k$也可以放草地且$k$与$j$不冲突则$dp[i][j]=dp[i][j]+dp[i-1][k]$

最后还是枚举存好的状态(不需要枚举1<<m次因为最后一行肯定也是合法的一定全在保存好的fit里面)把状态相加,最后想了一下为什么不是把每一行的每一种状态都加起来呢?因为你状态从$dp[i-1][k]$转移到$dp[i][j]$用的是+=已经把前面的状态算上去了。

最后送一组测试数据

3 3
1 1 1
1 0 1
1 1 1

答案是47

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=14;
const int mod=100000000;
int pos[N][N],Grass[N];
int dp[N][1<<N];
int fit[1<<N];
int tot,n,m,R; bool check(const int &a,const int &b)
{
return (a&b)==0;
}
bool check_grass(const int &grass,const int &v)
{
bitset<N> bg=grass,bv=v;
for (int i=0; i<N; ++i)
if(bg[i]==0&&bv[i]==1)
return false;
return true;
}
void init()
{
CLR(dp,0);
CLR(Grass,0); tot=0;
R=1<<m;
for (int i=0; i<R; ++i)
if(check(i,i<<1))
fit[tot++]=i;
}
int main(void)
{
int i,j,k;
while (~scanf("%d%d",&n,&m))
{
init();
for (i=0; i<n; ++i)
{
int bis=0;
for (j=0; j<m; ++j)
{
scanf("%d",&pos[i][j]);
bis=(bis<<1)+pos[i][j];
}
Grass[i]=bis;
} for (i=0; i<tot; ++i)
if(check_grass(Grass[0],fit[i]))
dp[0][fit[i]]=1; for (i=1; i<n; ++i)
{
for (j=0; j<tot; ++j)//第i行
{
if(check_grass(Grass[i],fit[j]))
{
for (k=0; k<tot; ++k)//枚举第i-1行
{
if(check_grass(Grass[i-1],fit[k])&&check(fit[k],fit[j]))//i-1行可放且与第i行不冲突
dp[i][fit[j]]+=dp[i-1][fit[k]];
}
}
}
}
int cnt=0;
for (i=0; i<tot; ++i)
{
cnt+=dp[n-1][fit[i]];
cnt%=mod;
}
printf("%d\n",cnt);
}
return 0;
}