[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]); } }