**POJ3254 Corn Fields ACM解题报告(状压dp)

人生第一个状压dp,虽然是看了题解艰难完成的,也算是把这题搞懂了,以前虽然稍微了解过状压dp,但是没想到实现起来也这么难。

这题就是给你的图中出现1的位置可以放牛,但是每个牛不能相邻(横竖相邻),一般状压的数据范围会比较小,然后就是用位运算把每行的情况都存起来。

这题首先要把1不会出现相邻的情况全部打表算出来,存储在state数组中(这个情况有377种)。

然后如果state[i]的每一个1的位置,在当前第j行的位置都是1,就是说第j行满足第i种情况,在dp[j][i]中存储这行这种情况的个数,如何来判断state[i]中的每个1在第j行中也是1呢,看了人家的题解发现真是精妙,将第j行的0出现的位置用位运算存起来,如果state[i]&a[j]为正,就是state[i]中的1在第j行的0的位置出现,那么肯定不满足这种情况了,如果运算结果位0,那么就是state[i]的1都出现在第j行的1的位置。

状态转移方程是dp(i,j)=sigma dp(i-1,k)state[j]&state[k]=0。(i表示行,j,k表示state中的第几种情况,然后这两种情况与运算等于0,就是竖直方向不能出现相邻)。

状压dp对于我来说还是很陌生啊,我得再搞明白几个题。

#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<stack>
using namespace std;
#define MAX 105
typedef long long LL;
const double pi=3.141592653589793;
const int INF=1e9;
const double inf=1e20;
const double eps=1e-6;
int dp[15][400];
int m,n,top;
int state[400];//所有1不相邻的情况先存起来
int a[15];//每行的情况(把0出现的位置存起来,和state中的情况进行位运算,如果非0,就是这行的情况不满足state中的这个情况)
bool ok(int x){//计算有多少种1不相邻的状态
	if(x&x<<1) return false;
	return true;
}
void init(){
	int total=1<<n;
	top=0;
	for(int i=0;i<total;i++){
		if(ok(i)) state[top++]=i;
	}
}
bool fit(int x,int y){
	if(x&a[y]) return 0;//判断这一行的状态是否包含state中满足条件的状态
	return 1;
}
int main(){
    scanf("%d%d",&m,&n);
    init();
    memset(dp,0,sizeof(dp));
    for(int i=0;i<m;i++){
	    for(int j=0;j<n;j++){
		    int x;
		    scanf("%d",&x);
		    if(x==0) a[i]+=(1<<j);//将0储存,因为需要state中的情况中的1完全包含于这一行的1,才算满足情况,将0储存做&运算比较方便
	    }
    }
    for(int i=0;i<top;i++){
	    if(fit(state[i],0)) dp[0][i]=1;//第一行的情况
    }
    for(int i=1;i<m;i++){
	    for(int k=0;k<top;k++){
		    if(fit(state[k],i)){//第i行满足的情况
			    for(int j=0;j<top;j++){
				    if(fit(state[j],i-1)){//上一行满足的情况
					    if(state[k]&state[j]) continue;//竖直方向不能有相邻的1
					    else dp[i][k]=(dp[i][k]+dp[i-1][j])%100000000;
				    }
			    }
		    }
	    }
    }
    int ans=0;
    for(int i=0;i<top;i++) ans=(ans+dp[m-1][i])%100000000;
    printf("%d\n",ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值