题目:
戳
题意:
有n*m大的一个地方,1表示土地肥沃可以种植物,0表示不能种植物,问:在不许有两个植物相邻的情况下,有多少种放置的方法。
分析:直接dp因为状态较多,数组很难直接表示出来,我们采用二进制状态压缩的方法来解决问题。
分析:
第一道 状压dp 题哦!
所以先做好准备工作:–位运算 tata~ 我自己整理了一丢丢
& 与 同为1为1 or 为0
| 或 同0为0 or 为1
^ 异或 不同为1 相同为0
~ 取反 去相反的
左移 << 右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2。
右移 同理
那么来说这道题,
我们用dp【i】【j】表示第i行的j的状态。
那么如果满足条件的话,dp【i】【j】+=sum(dp【i】【j】)
所以,我们还需要 判断 。
判断有两个条件:
第一是:
在能放的地方放。
那么我们将 土地的情况和我当前的枚举情况 进行一下 & 的运算 如果等于我的枚举情况,就可以放。
举个例子:
1 1 1 那么 其中一种可以放置的情况 就是 11 0
1 1 1
&1 1 0
1 1 0
再举个返利:
1 0 0 不能放置的 011
1 0 0
&0 1 1
0 0 0 不合法呀~
第二是:
相邻的地方不能 放置啊 这个就很好说了 如果你把当前状态左移一位,在和原来比较的 就是相邻位置 的情况啦 同样是 & 运算。 如果不是0 就不可以放。
至此,这道题最关键的地方就讲完了。
另外还有一步把输进的 转化为 10 进制。。。
下面看代码就好了【【【其实还没写,,现在去写、、、、
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
//by mars_ch
int n,m,ans;
int state[13];
int dp[13][1<<13]; //dp[i][j]表示第i行的j状态,要枚举状态
bool judge(int x,int y)
{
if((state[x] & y)!=y) return 0; //这是不能放置的,为什么看上边的解释啦
if((y&(y<<1))!= 0) return 0; // 相邻的是不可以的
else return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
state[i]=0;
for(int j=1;j<=m;j++)
{
int k;
scanf("%d",&k);
state[i]=(state[i]<<1)+k; // 把每一行都转成十进制数 eg 111 -> 7
}
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=(1<<m);j++)
{
if(judge(i,j) == 0) continue;
for(int k=0;k<=(1<<m);k++)
{
if(((j & k)!=0))continue;
dp[i][j]+=dp[i-1][k]; //第i-1行 不能和第i行 相邻的。
dp[i][j]%=100000000;
}
}
}
for(int i=0;i<=(1<<m);i++)
{
ans+=dp[n][i];
ans%=100000000;
}
printf("%d\n",ans);
return 0;
}