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

浙公网安备 33010602011771号