hdu1693 插头dp

学习插头dp 真是艰难,做的第一道题。
题意,现在给一个n*m的矩阵 矩阵中有一些障碍,障碍不能通过,现在要把能通过的点全部连起来,形成哈密顿回路,但是可以连成多个哈密顿回路,问有多少种方法。
直接说做法吧,首先是用轮廓线不懂的,可以先看看
看图
1
在轮廓线上每一位如果有一个插头那么这一位就表示位1没有就表示为0,插头是是在回路上的线,如果有插头表示上一格经过了这一条边,如图状态表示为101111当前转移的格子为第二行第三个,转移的时候主要看这个格子左边和上面的插头,如果都为1表示这两个连接到一起了,如果其中一个格子为1另一个为0 那么就是有两个出口如图,注意一下如果当前格子是障碍的话要特殊处理一下,只有在上插头和左插头是0的时候才能转移,因为没有办法经过这个格子
2
然后特殊处理一下最后一格,一行处理完后要把轮廓线移动到前面来,如图

这里写图片描述
具体操作看代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long
LL dp[2][1<<13];
int main()
{
    int t;
    scanf("%d",&t);
    int _case = 0;
    while(t--)
    {
        int dat[15][15];
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                scanf("%d",&dat[i][j]);
            }
        }
        memset(dp,0,sizeof(dp));
        int cur = 0;
        dp[cur][0] = 1;
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                for(int k = 0;k<(1<<(m+1));k++)
                {
                    if(j==m-1 && (k&1))continue;
                    int l = 1<<(m-j);
                    int u = l>>1;
                    if(dat[i][j])
                    {
                        dp[1-cur][k] = dp[cur][k^l^u];
                        if(((k&l)&&(k&u))==0 && ((k&l)||(k&u)))
                        {
                            dp[1-cur][k] += dp[cur][k];
                        }
                    }
                    else
                    {
                        if((k&l)==0 && (k&u)==0)
                        {
                            dp[1-cur][k] = dp[cur][k];
                        }
                    }
                }

                memset(dp[cur],0,sizeof(dp[cur]));
                cur = cur^1;
            }//cout << dp[cur][6] << endl;
            for(int k = 0;k<(1<<(m+1));k++) // 每完成一行后进行转移 其实就是把状态右移动一下,首行为0
            {
                if((k&1)==0)
                    dp[1-cur][k>>1] = dp[cur][k];
            }
            //cout << "k" << dp[1-cur][3] << endl;
            memset(dp[cur],0,sizeof(dp[cur]));
            cur = 1^cur;
        }
        printf("Case %d: There are %lld ways to eat the trees.\n",++_case,dp[cur][0]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值