【连通性状态压缩DP】hdu1693吃树

题意:给出一个n*m的地图,要用闭合回路经过每一个点(可以是多条),有一些格子是障碍格不能经过,问有多少种方案


分析:连通性状态压缩DP的模板题,首先引入“插头”和“轮廓线”的概念:

插头:表示与周围某个方向的格子有路径(如图红线)
轮廓线:分割已经决策过和未决策过的格子的分割线(如图蓝线,黄色是转移之前)
dp[i][j][S]表示当前枚举到第i行第j列(如图i=3,j=2)并且插头情况为S(用二进制表示,如图S={1,1,1,0,1}=29)
状态转移也很简单:
(1)如果转移后格子的插头经过目前分界线(如图黄线)的个数为2,即下一个状态这个格子不会再有插头,直接加当前方案数即可。
(2)如果转移后格子的插头经过目前分界线的个数为1,即下一个状态这个格子会有且仅有一个插头,(会有2种情况,分别是插头在下,插头在右,图示为插头在下,每种情况都要加)
(3)如果转移后格子的插头经过目前分界线的个数为0,即下一个状态这个格子一定会有2个插头,直接加即可。

障碍格只需要在转移时判断一下,如果是情况(1)或(2),即把目标赋值为0,如果是情况(3),则没有影响直接转移

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#include<cstdlib>
#include<queue>
#include<cmath>
#include<bitset>
#define SF scanf
#define PF printf
#define MAXN 15
#define MAXM 4200
using namespace std;
long long c,dp[MAXN][MAXN][MAXM],n,m,mat[MAXN][MAXN];
void work(){
    dp[0][m][0]=1;
    for(int i=1;i<=n;i++){
        for(int k=0;k<(1<<m);k++)
            dp[i][0][k<<1]=dp[i-1][m][k];
        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(mat[i][j]){
                    dp[i][j][k]=dp[i][j-1][k^q^p];
                    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;
                }
            }
    }
}
int main(){
    int t;
    SF("%d",&t);
    for(int num=1;num<=t;num++){
        SF("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                SF("%d",&mat[i][j]);
        memset(dp,0,sizeof dp);
        work();
        PF("Case %d: There are %lld ways to eat the trees.\n",num,dp[n][m][0]);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值