题目描述:
题意很简单,给你一张地图,地图上的1表示可以种玉米,为0的点表示不能种,同时相邻的点不能同时种玉米,问种玉米的方案总数。
这题对于状压的运用形式是非常不错的,我最开始甚至都没想好怎么状压,所以在这里详解一下这题的思维过程。对于每个点我们是非常不好处理的,那么我对于一行来看,我们无非是要在一行上若干个为1的点上种玉米,并且相邻的点不能同时种,数据限制N<=10,也就是说一行最多10个点,我们可以对这行所有的点进行状态压缩后进行枚举,那么在所有的状态中存在相邻点的状态就会被直接筛掉(要注意的是我们这里会枚举所有的状态,那么对于每一种状态我们默认的是该状态中所有的点都要种上玉米的,如果有点不种,那么实际上就是另一种状态了。),然后进一步筛选状态时,我们可以先把该行所有0的位置先二进制预处理出来,为什么先处理0的位置呢,因为我们如果处理1的位置的话,后面的状态其实是十分难判断的,比如该行1的位置为101的话,那么我们在枚举种玉米的状态时111,001两种状态都会被判断可行,实际上111很明显是不可行的状态。如果预处理0的位置的话,比如0的位置为101的话,那么很简单,所有与该位置有重叠的状态就都是不可行的了。那么通过这两步筛选,我们就可以很容易将一行所有可行的种玉米方案都枚举出来了。那么我们就可以从第一行开始,一行一行向下转移,对于某一行某一状态的方案总数其实就是所有可以转移得到该状态的状态方案总和,设dp[i][j]为第i行状态j的方案数的话,dp[i][j]+=sum(dp[i-1][k]),k为i-1行对应的状态,那么我们只需要将最后一行的数据求和就是最后的答案了。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXM=180;
const int MOD=100000000;
int M,N;
int e[12],s[1050],c[1050];
int dp[12][1050];
int main()
{
while(scanf("%d%d",&M,&N)!=EOF)
{
memset(e,0,sizeof(e));
int x;
for (int i=0;i<M;i++)
{
for (int j=0;j<N;j++)
{
scanf("%d",&x);
if (!x)
e[i]=e[i]|(1<<j);//预处理0的位置
}
}
int cnt=0;
for (int i=0;i<(1<<N);i++)//将不可行状态筛掉
{
if (i&(i<<1)) continue;
s[cnt]=i;
cnt++;
}
memset(dp,0,sizeof(dp));
for (int i=0;i<cnt;i++)//第一行的可行状态的方案数都为1
{
if (s[i]&e[0]) continue;
dp[0][i]=1;
}
for (int i=1;i<M;i++)
{
for (int j=0;j<cnt;j++)
{
if (e[i]&s[j]) continue;
for (int k=0;k<cnt;k++)
{
if (dp[i-1][k]==0) continue;
if (s[k]&s[j]) continue;//因为相邻的限制,所以如果上一行的状态与该行有重叠也不行
dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;
// cout<<dp[i][j]<<' '<<i<<' '<<j<<endl;
}
}
}
int ans=0;
for (int i=0;i<cnt;i++)
ans=(ans+dp[M-1][i])%MOD;
printf("%d\n",ans);
}
return 0;
}