原文:http://blog.csdn.net/y990041769/article/details/24658419
在原文基础上,加上自己的理解
状态压缩dp入门
1、二进制的移动知识基础:
#include <cstdio>
int main()
{
int k=5;
printf("%d\n", 1<<k);// 1 向左移动k位 100000,2^5 32
printf("%d\n", 1>>k);// 1 向右移动k位 0.00001,为0
printf("%d\n", k<<1);// k 向左移动1位 101 -> 1010 即k*2=10
printf("%d\n", k>>1);// k 向右移动1位 101 -> 10.1 即k/2=2
return 0;
}
2、& 的妙用
同为1 才为1,不然就是0,
很巧妙的相错开来
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=15;
const int M=1<<N;//10000000000000
const int mod = 100000000;
int st[M],mp[M];
int dp[N][M];//表示dp[i][j] 在i行 状态为j时 放牛的种数
int jud1(int x){
//相邻是否一样
return (x&(x<<1));
}
int jud2(int i,int x){
return (mp[i]&st[x]);
}
int main()
{
int n,m,x;
while(~scanf("%d%d",&n,&m)){
memset(st,0,sizeof(st));
memset(mp,0,sizeof(mp));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&x);
if(x==0)
//第i行的土地状况 可以累加出来
//而且这个土地情况 是 与真实的是相反的
mp[i]=mp[i]+(1<<(j-1));
}
}
int k=0;
//枚举m位 不同的0,1情况
//只有相邻不同的数,左移一位相互交错,数值为0
//st[] 存每一行相互交错的情况 ,总交错状态数为 k
for(int tm=0;tm<(1<<m);tm++){
if( !jud1(tm) )
st[k++]=tm;
}
for(int j=0;j<k;j++){
// 找出与第一行土地 状态为j ,且与土地交错的
// 那么初始化为1
if( !jud2(1,j) )
dp[1][j]=1;
}
for(int i=2;i<=n;i++){ //枚举行数
for(int j=0;j<k;j++){ //枚举上一行的状态数
//一:第i行状态j 与 第i行土地交错
if(jud2(i,j))
continue;
for(int f=0;f<k;f++){ //枚举下一行的状态数
//二:与第i行的上一行的土地也要交错
if(jud2(i-1,f))
continue;
//三:上一行状态j 和这一行状态f 也要交错
if(!(st[j]&st[f]))
dp[i][j]+=dp[i-1][f];
}
}
}
//dp[最后一行][不同方案] 总值累加起来,就是结果
int ans=0;
for(int j=0;j<k;j++){
ans+=dp[n][j];
ans%=mod;
}
printf("%d\n",ans);
}
return 0;
}