POJ 3254 - 状压dp

Magic Door

题目大意:

给一个地图,有障碍0,空地1,规定任意两头奶牛不能相邻且不能在障碍上面,问有多少种方法(一个都不放也算)。

题目分析

状态压缩: 首先将地图的每一行处理成用二进制表示Map[]。然后初始化每一行可能的情况(无用状态太多)存入State[].
dp[i][S]表示考虑到第i行且第i行状态为S的方案数。

\[dp[i][S] += dp[i - 1][valid S'] \]

中间进行些许剪枝(见代码)。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

const int N = 15, S = 40000, Mod = 100000000;
int n, m;
int Map[N];
typedef long long ll;
int state[S], scnt, ans;
int dp[N][S];

inline int DP(int u, int st){
	if(u == 1) return dp[1][st] = 1;
	if(dp[u][st] != -1) return dp[u][st];
	int t = 0;
	for(int i = 1; i <= scnt; i++){
		if((state[i] & Map[u - 1]) || (state[i] & st)) continue;
		t += DP(u - 1, state[i]);
	}
	return dp[u][st] = t;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);
	cin >> n >> m;
	memset(dp, -1, sizeof dp);
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++){
			int x; cin >> x;
			if(!x) Map[i] |= (1 << (j - 1));
		}
	for(int s = 0; s < (1 << m); s++){
		if(s & (s << 1)) continue;
		state[++scnt] = s;
	}
	for(int i = 1; i <= scnt; i++){
//		int s = state[i]; cout<<state[i]<<" ";
//		while(s) cout<<s%2, s /= 2;cout<<endl;
		if(state[i] & Map[n]) continue;
		ans = (ans + DP(n, state[i])) % Mod;
	}
	cout << ans << endl;
	return 0;
}
posted @ 2017-10-07 23:58  CzYoL  阅读(159)  评论(0编辑  收藏  举报