[HDU 1693 Eat the Trees]轮廓线DP(插头dp)

插头dp,在1218大神讲了之后,我能深沉体会到它的恶心orz,这道题其实还好,觉得插头dp维护轮廓线的想法还是十分的精妙 hdu1693 大意:(DOTA相关)屠夫大后期没用啦,只能吃吃大树维持生活。这张n*m图有一些障碍(不可到达),求除了障碍的树全都吃掉,并且走的都是回路的方案数。(n,m 12) 插头dp的插头表示一种连通状态,并且表示一种确认的连通状态(即已经连上而非是将要连上) 我们状态 f[i][j][S]决策点在(i,j)并且轮廓线的状态为S方案数。这个轮廓线是经过这个方格的下方和右方的线,线连出去的插头。由于这道题不需要插头之间连通性,所以我们可以二维状态直接表示出来是否有插头在。 状态定好之后根据决策点的选择与状态变化写出来就是了。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define int long long
using namespace std;
int f[15][15][1<<12];
int n,m;
int S;
int a[15][15];
main() {
	int  t; scanf("%lld",&t);
	
	for(int woc=1;woc<=t;woc++){
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=m;j++) {
				scanf("%lld",&a[i][j]);
			}
		}
		memset(f,0,sizeof f);
		S = (1<<(m+1))-1;
		f[0][m][0] = 1;
		for(int o=1;o<=n;o++) {
			for(int j=0;j<(1<<m);j++) {
				f[o][0][j<<1] = f[o-1][m][j];
			}
			for(int j=1;j<=m;j++) {
				for(int s=0;s<=S;s++) { //enumerate outline
					bool lef = (s>>(j-1))&1; bool up = (s>>j)&1;
					int zy = f[o][j-1][s];
					if(!a[o][j]) {
						if((!lef)&&(!up)) f[o][j][s] += zy;
					} else {
						int xx = (1<<(j-1))|(1<<j);
						if(lef^up) {
							f[o][j][s^xx] += zy; f[o][j][s] += zy;
						} else {
							f[o][j][s^xx] += zy;
						}
					}
				}
			}
		}
		printf("Case %lld: There are %lld ways to eat the trees.\n",woc,f[n][m][0]);
	}
}
 
posted @ 2019-01-06 13:33  Newuser233  阅读(5)  评论(0)    收藏  举报