poj3254状压dp

【题目大意】一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)


【解析】根据题意,把每一行的状态用二进制的数表示,0代表不在这块放牛,1表示在这一块放牛。首先很容易看到,每一行的状态要符合牧场的硬件条件,即牛必须放在能放牧的方格上。这样就能排除一些状态。另外,牛与牛之间不能相邻,这样就要求每一行中不能存在两个相邻的1,这样也能排除很多状态。然后就是根据上一行的状态转移到当前行的状态的问题了。必须符合不能有两个1在同一列(两只牛也不能竖着相邻)的条件。这样也能去掉一些状态。然后,上一行的所有符合条件的状态的总的方案数就是当前行该状态的方案数。

【状态表示】dp[state][i]:在状态为state时,到第i行符合条件的可以放牛的方案数

【状态转移方程】dp[state][i] =Sigma dp[state'][i-1] (state'为符合条件的所有状态)

【DP边界条件】首行放牛的方案数dp[state][1] =1(state符合条件) OR 0 (state不符合条件)



================================================================

以上为复制  http://blog.youkuaiyun.com/accry/article/details/6607703


找到state数组,储存牛不相邻的状态。

输入,储存当前图。

初始化dp第一行,遍历state数组,如果该state符合当前图,dp【行】【状态】=1,否则为0。

更新dp:

对第i行来说,找到一个i行符合当前图的状态state【k】,

dp[i][state[k]]是∑dp[i-1][state【(1.该状态符合第i-1行的图2.该状态与state【k】不上下相邻)】]



#include "cstdio"
#include "iostream"
#include "cstring"
using namespace std;
#define mod 100000000
int m,n,top;
int state[1<<15];
int dp[15][1<<15];
int map[15];

inline bool ok(int x){
//judge whether there are two cows stay together
	if(x&(x<<1)) return false;
	return true;
}
inline bool fit(int s,int c){
	if((s^c)==c-s) return true;
	return false;
}
void init(){
	top=0;
	int total=1<<n;
	for(int i=0;i<total;i++)
	{
		if(ok(i)) state[++top]=i;
	}
}
int main()
{
	while(~scanf("%d%d",&m,&n))
	{
		init();
		memset(dp,0,sizeof(dp));
		memset(map,0,sizeof(map));
		for(int i=1;i<=m;i++)
			for(int j=1;j<=n;j++){
				int cur;
				scanf("%d",&cur);
					map[i]=(map[i]<<1)+cur;
			}
		// init the first line of dp
		for(int i=1;i<=top;i++)
			if(fit(state[i],map[1])) dp[1][state[i]]=1;

		for(int i=2;i<=m;i++){// next line
			for(int k=1;k<=top;k++){//current line state
				if(!fit(state[k],map[i])) continue;
				for(int j=1;j<=top;j++){//last line state
					if(!fit(state[j],map[i-1])) continue;
					if(state[k]&state[j]) continue;
					dp[i][state[k]]=(dp[i][state[k]]+dp[i-1][state[j]])%mod;
				}
			}
		}
		int ans=0;
		for(int i=1;i<=top;i++)
			ans=(ans+dp[m][state[i]])%mod;
		printf("%d\n",ans );
	}


	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值