【1】POJ3254 Corn Fields
题目大意:
一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛(不包括斜着的),即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)
输入
1<=n<=12,1<=m<=12
输出
一个mod100000000的整数
样例输入
2 3
1 1 1
0 1 0
样例输出
9
分析
从题意我们可以知道牛与牛之间不能相邻,我们可以很容易的想到可以一行一行的递推,因为每只牛能不能放只与上一行和当前这一行有关。
所以dp中有一个维度是用来表示第几行的,还有一个维度就是用来表示哪一行的状态的。
假设有m列,则状态最多只有(1<<m-1)种,这是显然的。因为他每一列只有放和不放这两种决策。
那么怎么表示状态是个关键问题,这就要用到状态压缩了,用二进制来表示某个状态,比如
0101代表的就是1、3列不放牛,2、4列放牛。这样不仅省空间省代码还省时间。
要用位运算是必须的。
参考分析
重点理解二进制的用处
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1e4;
const int mod=1e9;
int corn[15],state[N];
int dp[15][N];
void init(int n,int &num)
{
int all=(1<<n);//不考虑题目,n个二进制位所能表示的最大数+1
for(int i=0;i<all;i++)
{
if(!(i&(i<<1)))//记下所有二进制位为0,1间隔的数
state[++num]=i;
}
}
int main()
{
int m,n;
while(scanf("%d%d",&m,&n)!=EOF)
{
int c,num=0;//暂时满足条件的状态数
memset(corn,0,sizeof(corn));//每一行的最大状态表示数
memset(state,0,sizeof(state));//所有满足题目要求的状态
memset(dp,0,sizeof(dp));
init(n,num);//cout<<num<<endl;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&c);
if(c==0)//取反,和题意相反,便于后续处理
corn[i]+=(1<<(n-j));
}
}
for(int i=1;i<=num;i++)
{
if(!(state[i]&corn[1]))
dp[1][i]=1;//先确定第一行,初值
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=num;j++)
{
if(state[j]&corn[i])
continue;
for(int k=1;k<=num;k++)
{
if(state[k]&corn[i-1])
continue;
if(state[k]&state[j])
continue;
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
}
}
}
int ans=0;
for(int i=1;i<=num;i++)
ans=(ans+dp[m][i])%mod;
printf("%d\n",ans);
}
return 0;
}
【2】POJ3311 Hie With The Pie
【3】POJ1185 炮兵阵地