HDOJ 1693

题意:给定N*M的矩阵,里面有些有树,有些没树(用0表示),问有多少种方法,通过任意个不交叉环路把树全连起来(就是哈密顿回路或者多个哈密顿回路覆盖所有非0数)

题解:插头DP入门题,实际上,感觉插头DP也就是状态压缩DP的一种特例而已。若要学插头DP,请参见《基于连通性状态压缩的动态规划问题》,看了它,也就知道一些基本概念和解题思路了。

这道题我的方法是dp[i][j][st]代表第i行格子,进行到第j个竖线(该格子左方),轮廓线插头状态为st时的状态数(1代表有插头,0代表没插头)。

当map[i][j+1]为0时,看st对应的两个插头是否为1,因为不能经过(i,j+1)这个格子,所以有插头的状态均淘汰掉,可行状态则直接转移,dp[i][j+1][st]+=dp[i][j][st]

当map[i][j+1]不为0时,看st的两个插头:

1、都是1,这个格子的插头状况也就唯一确定了,直接推出dp[i][j+1][st-a-b]+=dp[i][j][st];(a,b为相关插头的二进制编码)

2、都是0,由于回路经过的格子必有两个插头,这个格子的插头也就确定了,dp[i][j+1][st|a|b]+=dp[i][j][st];

3、只有一个1,那么转移就有两种可能,向左或者向下,即dp[i][j+1][(st&(~a))|b]+=dp[i][j][st];dp[i][j+1][(st&(~b))|a]+=dp[i][j][st];

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
int map[13][13],n,m;
LL dp[13][13][(1<<13)+1];
int main()
{
    int T,ca=0;
    for(scanf("%d",&T);T;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;
        int tot=1<<(m+1),haf=1<<m;
        for(int i=1;i<=n;i++)
        {
            for(int tp=0;tp<haf;tp++)
               dp[i][0][tp]=dp[i-1][m][tp<<1];
            for(int j=0;j<m;j++)
            {
                for(int st=0;st<tot;st++)
                {
                    if(!dp[i][j][st])
                        continue;
                    int x=i,y=j+1,a=1<<(m-y),b=1<<(m-y+1);
                    if(map[x][y]==0)
                    {
                        if((st&a)||(st&b))
                            continue;
                        dp[i][j+1][st]+=dp[i][j][st];
                    }
                    else if((a&st)||(b&st))
                    {
                        if((a&st)&&(b&st))
                            dp[i][j+1][st-a-b]+=dp[i][j][st];
                        else
                        {
                            dp[i][j+1][(st&(~a))|b]+=dp[i][j][st];
                            dp[i][j+1][(st&(~b))|a]+=dp[i][j][st];
                        }
                    }
                    else
                    {
                        dp[i][j+1][st|a|b]+=dp[i][j][st];
                    }
                }
            }
        }
        printf("Case %d: There are %I64d ways to eat the trees.\n",++ca,dp[n][m][0]);
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值