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;
}

 

posted @ 2017-03-16 17:29  karles~  阅读(242)  评论(0编辑  收藏  举报