Eat the Trees(hdu 1693)
题意:在n*m的矩阵中,有些格子有树,没有树的格子不能到达,找一条或多条回路,吃完所有的树,求有多少中方法。
第一道真正意义上的插头DP,可参考陈丹琦的《基于连通性状态压缩的动态规划问题》,虽然我看了一遍,但只是了解了个大概,主要还是看别人的代码,自己画图理解。
插头和轮廓线的定义就不说了,在PPT中很好理解。
先说一下转移的方式,如下图(p和q是当前枚举到的格子所面临的需要更新插头的地方):
通过在纸上画图,可以得出这么几个结论:
前一个状态的q和p都有插头的话,后一个状态的q和p都不能有插头。(因为一个显然格子只能有两个插头)
前一个状态的q和p都没有插头的话,后一个状态的q和p都必须有插头。(因为每个格子必须经过)
前一个状态的q和p任何一个有插头,后一个状态的q和p中只有一个有插头。
得到这几个结论之后,就可以方便地转移了。
最后在代码中有一点要注意的是,上一行的最后一列的状态可以转移到下一行第0列的状态,原因如下图:
代码参考于:http://www.cnblogs.com/zhuangli/archive/2008/09/04/1283753.html
#include<cstdio> #include<iostream> #include<cstring> #define N 12 using namespace std; int map[N][N],n,m; long long dp[N][N][1<<N]; int main(){ int T,cas=0;scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&map[i][j]); memset(dp,0,sizeof(dp)); dp[0][m][0]=1; for(int i=1;i<=n;i++){ for(int j=0;j<(1<<m);j++) dp[i][0][j<<1]=dp[i-1][m][j]; for(int j=1;j<=m;j++){ for(int k=0;k<(1<<m<<1);k++){ int p=1<<j; int q=p>>1; bool x=k&p; bool y=k&q; if(map[i][j]){ dp[i][j][k]+=dp[i][j-1][k^p^q]; if(x!=y) dp[i][j][k]+=dp[i][j-1][k]; } else { if(x==0&&y==0) dp[i][j][k]=dp[i][j-1][k]; else dp[i][j][k]=0; } } } } printf("Case %d: There are %I64d ways to eat the trees.\n",++cas,dp[n][m][0]); } return 0; }