HDU 1693 插头dp入门详解

放题目链接   https://vjudge.net/problem/22021/origin

给出一个n*m的01矩阵,1可走0不可通过,要求走过的路可以形成一个环且可以有多个环出现,问有多少不同的行走方案;

这道题目可以有多个环而不是限制为一个环,大大减弱了题目的难度,我还是想了好久。

先简单介绍一下轮廓线及插头,

这种dp进行状态转移时,每个方格对应着他自己的一条轮廓线,由上一个方格的轮廓线的状态,推导当前方格的轮廓线状态。

 

 

 

 

 

 

    K1

       K0

 

 

 K2  3,3

 

      K4

    K3

 

 

 

 

 

 

做的图不兼容只能这样看了,对于正在推倒的方格(3,3),蓝色那条轮廓线就是前一个方格(3,2)的轮廓线,根据这条蓝线的状态推导(3,3)的轮廓线状态,

由于每个格子都要通过,所以只要标记为'1'的格子对应的轮廓线上肯定要有插头,不然就不合法了,也就是说每个线只有两个情况0/1,

我们用一个整数的二进制表示这条线,假设x的二进制为k0k1k2k3k4,对应图中的蓝线旁的字符,进行状态压缩。

横线如果有插头,就是上下方向的,竖线就是左右方向的,能决定当前格子的就是k1和k2位了,其他位置不影响穿过当前格子的插头。

假设此格子是非障碍格,

当k1=k2=1时,表示有一个拐角状的插头穿过了这个格,显然这个格子不能再有插头了,将蓝线状态中的k1-->0,k2-->0,就是更新后的状态,让他加上蓝线状态的方案数

当k1=k2=0时,表示没有插头穿过格子,所以我们要给他加上一个拐角作为初始格,将k1->1,k2->1即可。

当k1=0,k2=1时,这个插头可以向右走或向下走,判断一下这两种情况是否合法,是的话进行转移。

当k1=1,k2=0时,同上。

如果这是个障碍格,只有k1=k2=0时才能转移,因为障碍格一定没插头。

还有一点要说的是由上一行最后一个状态向下一行第一个状态转移时,

由于上一行最后一个状态中k0位一定是0(边界定无右插头),下一行第一个的k4也一定是0

所以直接右移一位即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define LL long long
 4 int N,M,e[15][15];
 5 LL dp[2][1<<12];
 6 LL solve()
 7 {
 8     memset(dp,0,sizeof(dp));
 9     int cur=0,i,j,k;
10     dp[0][0]=1;
11     for(i=1;i<=N;++i)
12     {
13         cur^=1;
14         memset(dp[cur],0,sizeof(dp[cur]));
15         for(k=0;k<(1<<M);++k)
16             dp[cur][k<<1]=dp[cur^1][k];
17         for(j=1;j<=M;++j)
18         {
19             cur^=1;
20             memset(dp[cur],0,sizeof(dp[cur]));
21             for(k=0;k<(1<<(M+1));++k)
22             {
23             if(!dp[cur^1][k]) continue;
24             int y=k&(1<<(j-1)),y1=1<<(j-1);
25             int x=k&(1<<j),x1=1<<j;
26             if(e[i][j]){
27                if(x&&y) dp[cur][k^x1^y1]+=dp[cur^1][k];
28                if(!(x+y)) dp[cur][k|x1|y1]+=dp[cur^1][k];
29                if(x&&(!y)){
30                 if(j<M&&e[i][j+1]) dp[cur][k]+=dp[cur^1][k];
31                 if(i<N&&e[i+1][j]) dp[cur][k^x1|y1]+=dp[cur^1][k];
32                }
33                if(!x&&y){
34                 if(j<M&&e[i][j+1]) dp[cur][k^y1|x1]+=dp[cur^1][k];
35                 if(i<N&&e[i+1][j]) dp[cur][k]+=dp[cur^1][k];
36                }
37             }
38             else{
39               if(!(x+y)) dp[cur][k]+=dp[cur^1][k];
40             }
41             }
42         }
43     }
44 
45     return dp[cur][0];
46 }
47 int main()
48 {
49     int t,i,j;
50     scanf("%d",&t);
51     for(int cas=1;cas<=t;++cas){
52     memset(e,0,sizeof(e));
53         scanf("%d%d",&N,&M);
54         for(i=1;i<=N;++i)
55             for(j=1;j<=M;++j) scanf("%d",&e[i][j]);
56         printf("Case %d: There are %lld ways to eat the trees.
",cas,solve());
57     }
58     return 0;
59 }

 

 

 

 

 

 

    K1

       K0

 

 

 K2  3,3

 

      K4

    K3

 

 

 

 

 

 

 

                   
   
 
   
   
 
 
 
   
 

 

原文地址:https://www.cnblogs.com/zzqc/p/7253991.html