插头DP

来源:互联网 发布:服务器端口怎么看 编辑:程序博客网 时间:2024/04/28 01:03

转载自:http://blog.csdn.net/sf____/article/details/15026397


最近两周一直在刷DP题,前几天接触了插头DP。说实话,直接做“入门”题Ural 1519 Formula 1难度略大,而网上也没有个由浅入深的题表和教程。故总结了一下最近做的、适合作为插头DP专题入门题的题目,专心写一篇博客。大牛见笑。

    学习插头DP前,你得搞清楚状态压缩DP是什么。这里推荐AcCry的一篇状态压缩教程:状态压缩总结。刷完教程里的8题之后,状态压缩DP也就是入门了,也就可以开始学习插头DP了。



POJ 2411 Mondriaan's Dream

    1*2砖填充矩形,问多少种方法。这道题一般想到的方法都是状态压缩DP,上面AcCry的教程中也有这题。但是有更快的方法(不是打表= =),我们可以不按照一行一行匹配、转移的做法,而是一格一格直接转移。这是我第一次接触插头DP,在这篇解题报告上看到的:http://blog.csdn.net/fp_hzq/article/details/6427072。他写的代码很短,POJ上16MS搞定,让我顿时来了兴趣。不过他的代码真的不好理解。研究了很久,画个图给大家看吧。我们可以用1表示该处竖着放一块砖,用0表示横着放的砖,或者竖着放的第二行。或者这样说,1表示下一行此处不可以放砖,0表示下一行此处可以放砖。状态的转移有两种。见下图。

状态转移图

    然后,代码如下:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5.   
  6. long long dp[2][1<<11];  
  7.   
  8. int main()  
  9. {  
  10.     int n,m;  
  11.     while(scanf("%d%d",&n,&m),(n||m))  
  12.     {  
  13.         int total=1<<m;  
  14.         int pre=0,now=1;  
  15.         memset(dp[now],0,sizeof(dp[now]));  
  16.         dp[now][0]=1;  
  17.   
  18.         for(int i=0;i<n;i++)  
  19.             for(int j=0;j<m;j++)  
  20.         {  
  21.             swap(now,pre);  
  22.             memset(dp[now],0,sizeof(dp[now]));  
  23.   
  24.             for(int S=0;S<total;S++) if( dp[pre][S] )  
  25.             {  
  26.                 dp[now][S^(1<<j)]+=dp[pre][S];  
  27.                 if( j && S&(1<<(j-1)) && !(S&(1<<j)) )  
  28.                     dp[now][S^(1<<(j-1))]+=dp[pre][S];  
  29.             }  
  30.         }  
  31.   
  32.         printf("%lld\n",dp[now][0]);  
  33.     }  
  34. }  
    这是初学的时候写的代码,下面给出模板化的代码:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5. #define LL long long   
  6.   
  7. const int maxn=2053;  
  8. struct Node  
  9. {  
  10.     int H[maxn];  
  11.     int S[maxn];  
  12.     LL N[maxn];  
  13.     int size;  
  14.     void init()  
  15.     {  
  16.         size=0;  
  17.         memset(H,-1,sizeof(H));  
  18.     }  
  19.   
  20.     void push(int SS,LL num)  
  21.     {  
  22.         int s=SS%maxn;  
  23.         while( ~H[s] && S[H[s]]!=SS )  
  24.             s=(s+1)%maxn;  
  25.   
  26.         if(~H[s])  
  27.         {  
  28.             N[H[s]]+=num;  
  29.         }  
  30.         else  
  31.         {  
  32.             S[size]=SS;  
  33.             N[size]=num;  
  34.             H[s]=size++;  
  35.         }  
  36.     }  
  37.   
  38.     LL get(int SS)  
  39.     {  
  40.         int s=SS%maxn;  
  41.         while( ~H[s] && S[H[s]]!=SS )  
  42.             s=(s+1)%maxn;  
  43.   
  44.         if(~H[s])  
  45.         {  
  46.             return N[H[s]];  
  47.         }  
  48.         else  
  49.         {  
  50.             return 0;  
  51.         }  
  52.     }  
  53. } dp[2];  
  54. int now,pre;  
  55.   
  56. int get(int S,int p,int l=1)  
  57. {  
  58.     if(p<0) return 0;  
  59.     return (S>>(p*l))&((1<<l)-1);  
  60. }  
  61.   
  62. void set(int &S,int p,int v,int l=1)  
  63. {  
  64.     S^=get(S,p,l)<<(p*l);  
  65.     S^=(v&((1<<l)-1))<<(p*l);  
  66. }  
  67.   
  68. int main()  
  69. {  
  70.     int n,m;  
  71.     while( scanf("%d%d",&n,&m),n||m )  
  72.     {  
  73.         if(n%2 && m%2) {puts("0");continue;}  
  74.   
  75.         int now=1,pre=0;  
  76.         dp[now].init();  
  77.         dp[now].push(0,1);  
  78.   
  79.         for(int i=0;i<n;i++) for(int j=0;j<m;j++)  
  80.         {  
  81.             swap(now,pre);  
  82.             dp[now].init();  
  83.   
  84.             for(int s=0;s<dp[pre].size;s++)  
  85.             {  
  86.                 int S=dp[pre].S[s];  
  87.                 LL num=dp[pre].N[s];  
  88.                 int p=get(S,j);  
  89.                 int q=get(S,j-1);  
  90.   
  91.                 int nS=S;  
  92.                 set(nS,j,1-p);  
  93.                 dp[now].push(nS,num);  
  94.   
  95.                 if(p==0 && q==1)  
  96.                 {  
  97.                     set(S,j-1,0);  
  98.                     dp[now].push(S,num);  
  99.                 }  
  100.             }  
  101.         }  
  102.   
  103.         printf("%lld\n",dp[now].get(0));  
  104.     }  
  105. }  
    按照Kuangbin大神说的,自己慢慢形成自己的模板风格就好了。

    这一题,主要学习逐格递推的方式。状态压缩DP是枚举两行的状态,找到匹配的状态后转移,复杂度为n*2^2n。而逐格转移省去了很多无用的状态,复杂度也小了很多,为n^2*2^n。


HDU 1565 方格取数(1)

    依旧是状态压缩DP可以做的题目。取一个数字,周围四个数字不可取。做法类似于上题。取数字的位置标记为1,不取标记为0。转移时,我一定可以不取。如果当前行上一行没有取数字,左边的一格没有取数字,这一格我可以取。代码如下:

[cpp] view plain copy
 print?
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5.   
  6. int dp[2][1<<20];  
  7.   
  8. int main()  
  9. {  
  10.     int n;  
  11.     while(~scanf("%d",&n))  
  12.     {  
  13.         int total=1<<n;  
  14.         int now=1,pre=0;  
  15.         for(int S=0;S<total;S++)  
  16.             dp[now][S]=-1;  
  17.         dp[now][0]=0;  
  18.   
  19.         for(int i=0;i<n;i++)  
  20.             for(int j=0;j<n;j++)  
  21.         {  
  22.             swap(now,pre);  
  23.             for(int S=0;S<total;S++)  
  24.                 dp[now][S]=-1;  
  25.               
  26.             int v;  
  27.             scanf("%d",&v);  
  28.             for(int S=0;S<total;S++) if(~dp[pre][S])  
  29.             {  
  30.                 int not=S&(~(1<<j));  
  31.                 dp[now][not]=max(dp[now][not],dp[pre][S]);  
  32.                 if(!(S&(1<<j)) && ( j==0 || (j>0 && !(S&(1<<(j-1)))) ))  
  33.                     dp[now][S^(1<<j)]=max(dp[pre][S]+v,dp[now][S^(1<<j)]);  
  34.             }  
  35.         }  
  36.   
  37.         int ans=0;  
  38.         for(int S=0;S<total;S++) if(~dp[now][S])  
  39.             ans=max(ans,dp[now][S]);  
  40.         printf("%d\n",ans);  
  41.     }  
  42. }  
    模板化代码:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. #include <iostream>  
  5. using namespace std;  
  6. #define LL long long   
  7. const int maxn=10011;  
  8.   
  9. struct Node  
  10. {  
  11.     int H[maxn];  
  12.     int S[maxn];  
  13.     int N[maxn];  
  14.     int size;  
  15.     void init()  
  16.     {  
  17.         size=0;  
  18.         memset(H,-1,sizeof(H));  
  19.     }  
  20.   
  21.     void push(int SS,int num)  
  22.     {  
  23.         int s=SS%maxn;  
  24.         while( ~H[s] && S[H[s]]!=SS )  
  25.         {  
  26.             s++;  
  27.             s%=maxn;  
  28.         }  
  29.   
  30.         if(~H[s])  
  31.         {  
  32.             N[H[s]]=max(N[H[s]],num);  
  33.             return;  
  34.         }  
  35.   
  36.         N[size]=num;  
  37.         S[size]=SS;  
  38.         H[s]=size++;  
  39.     }  
  40. } dp[2];  
  41. int now,pre;  
  42.   
  43. int get(int S,int p,int l=1)  
  44. {  
  45.     return (S>>(p*l))&((1<<l)-1);  
  46. }  
  47.   
  48. void set(int &S,int p,int v,int l=1)  
  49. {  
  50.     S^=get(S,p,l)<<(p*l);  
  51.     S^=(v&((1<<l)-1))<<(p*l);  
  52. }  
  53.   
  54. int main()  
  55. {  
  56.     int n;  
  57.     while(~scanf("%d",&n))  
  58.     {  
  59.         now=1;  
  60.         pre=0;  
  61.         dp[now].init();  
  62.         dp[now].push(0,0);  
  63.   
  64.         for(int i=0;i<n;i++)  
  65.             for(int j=0;j<n;j++)  
  66.         {  
  67.             int v;  
  68.             scanf("%d",&v);  
  69.   
  70.             swap(now,pre);  
  71.             dp[now].init();  
  72.   
  73.             for(int s=0;s<dp[pre].size;s++)  
  74.             {  
  75.                 int S=dp[pre].S[s];  
  76.                 int num=dp[pre].N[s];  
  77.   
  78.                 if( get(S,j)==0  
  79.                     && ( j==0 || get(S,j-1)==0 ) )  
  80.                 {  
  81.                     set(S,j,1);  
  82.                     dp[now].push(S,v+num);  
  83.                 }  
  84.   
  85.                 set(S,j,0);  
  86.                 dp[now].push(S,num);  
  87.             }  
  88.         }  
  89.   
  90.         nth_element(dp[now].N,dp[now].N+dp[now].size-1,dp[now].N+dp[now].size);  
  91.         printf("%d\n",dp[now].N[dp[now].size-1]);  
  92.     }  
  93. }  



HDU 2167 Pebbles

    相对上一题方格取数,这一题要高级很多了。首先输入比较难处理,其次周围8格数字不可取,上一题的方法不能直接使用了。因为必须要记录当前格左上角的数字有没有取得情况,我们需要在状态中加一位,并且换行时要更新一下状态。如下图:

状态记录及转移

    在取11格的数字时,我们需要判断10,6,7,8格是否有取数字。在更新11格的状态的同时,丢弃掉第6格的状态值。在遇到换行时,第8格的状态可以直接丢弃,但是我们要虚拟出一个新的格,方便下一次的状态转移。这题要理解多加的状态,以及换行时的状态变化。此时的状态记录已经类似于下面说的轮廓线了。代码如下:

[cpp] view plain copy
 print?
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5.   
  6. int dp[2][1<<16];  
  7.   
  8. int main()  
  9. {  
  10.     while(1)  
  11.     {  
  12.         int n=16;  
  13.         int t=0;  
  14.         int total=1<<n;  
  15.         int now=1,pre=0;  
  16.         for(int S=0;S<total;S++)  
  17.             dp[now][S]=-1;  
  18.         dp[now][0]=0;  
  19.   
  20.         for(int i=0;i<n;i++)  
  21.             for(int j=0;j<n;j++)  
  22.         {  
  23.             int v;  
  24.             char ch;  
  25.             if(scanf("%d%c",&v,&ch)==-1)  
  26.                 return 0;  
  27.             t++;  
  28.             if(ch=='\n' && n==16) n=t,total=1<<(n+1);  
  29.   
  30.             swap(now,pre);  
  31.             for(int S=0;S<total;S++)  
  32.                 dp[now][S]=-1;  
  33.               
  34.             for(int S=0;S<total;S++) if(~dp[pre][S])  
  35.             {  
  36.                 if(j==0)  
  37.                 {  
  38.                     int SS=(S<<1)&(~(1<<(n+1)));  
  39.                     dp[now][SS&(~1)]=max(dp[pre][S],dp[now][SS&(~1)]);  
  40.                     if( !(S&(1<<j)) && !(S&(1<<(j+1))) )  
  41.                         dp[now][SS^(1<<j)]=max(dp[pre][S]+v,dp[now][SS^(1<<j)]);  
  42.                     continue;  
  43.                 }  
  44.   
  45.                 dp[now][S&(~(1<<j))]=max(dp[pre][S],dp[now][S&(~(1<<j))]);  
  46.   
  47.                 if(!(S&(1<<j)) && !(S&(1<<(j-1))) && !(S&(1<<(j+1))) && !(S&(1<<(j+2))) )  
  48.                     dp[now][S^(1<<j)]=max(dp[pre][S]+v,dp[now][S^(1<<j)]);  
  49.             }  
  50.         }  
  51.         nth_element(dp[now],dp[now]+total-1,dp[now]+total);  
  52.         printf("%d\n",dp[now][total-1]);  
  53.     }  
  54. }  
    模板化代码如下:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. #include <iostream>  
  5. using namespace std;  
  6. #define LL long long   
  7. const int maxn=100013;  
  8. int maze[16][16];  
  9.   
  10. struct Node  
  11. {  
  12.     int H[maxn];  
  13.     int S[maxn];  
  14.     int N[maxn];  
  15.     int size;  
  16.     void init()  
  17.     {  
  18.         size=0;  
  19.         memset(H,-1,sizeof(H));  
  20.     }  
  21.   
  22.     void push(int SS,int num)  
  23.     {  
  24.         int s=SS%maxn;  
  25.         while( ~H[s] && S[H[s]]!=SS )  
  26.         {  
  27.             s++;  
  28.             s%=maxn;  
  29.         }  
  30.   
  31.         if(~H[s])  
  32.         {  
  33.             N[H[s]]=max(N[H[s]],num);  
  34.             return;  
  35.         }  
  36.   
  37.         N[size]=num;  
  38.         S[size]=SS;  
  39.         H[s]=size++;  
  40.     }  
  41. } dp[2];  
  42. int now,pre;  
  43.   
  44. int get(int S,int p,int l=1)  
  45. {  
  46.     return (S>>(p*l))&((1<<l)-1);  
  47. }  
  48.   
  49. void set(int &S,int p,int v,int l=1)  
  50. {  
  51.     S^=get(S,p,l)<<(p*l);  
  52.     S^=(v&((1<<l)-1))<<(p*l);  
  53. }  
  54.   
  55. int main()  
  56. {  
  57.     while(1)  
  58.     {  
  59.         int n=16;  
  60.         for(int i=0;i<n;i++)  
  61.             for(int j=0;j<n;j++)  
  62.         {  
  63.             char ch;  
  64.             if(scanf("%d%c",&maze[i][j],&ch)==-1) return 0;  
  65.             if(ch=='\n' && n==16) n=j+1;  
  66.         }  
  67.   
  68.         int now=1,pre=0;  
  69.         dp[now].init();  
  70.         dp[now].push(0,0);  
  71.   
  72.         for(int i=0;i<n;i++)  
  73.         {  
  74.             for(int j=0;j<n;j++)  
  75.             {  
  76.                 swap(now,pre);  
  77.                 dp[now].init();  
  78.   
  79.                 for(int s=0;s<dp[pre].size;s++)  
  80.                 {  
  81.                     int S=dp[pre].S[s];  
  82.                     int num=dp[pre].N[s];  
  83.   
  84.                     if( get(S,j+1)==0   
  85.                      && (j==0 || (get(S,j)==0 && get(S,j-1)==0))  
  86.                      && get(S,j+2)==0 )  
  87.                     {  
  88.                         set(S,j,1);  
  89.                         dp[now].push(S,num+maze[i][j]);  
  90.                     }  
  91.   
  92.                     set(S,j,0);  
  93.                     dp[now].push(S,num);  
  94.                 }  
  95.             }  
  96.   
  97.             for(int s=0;s<dp[now].size;s++)  
  98.                 set(dp[now].S[s],n,0),dp[now].S[s]<<=1;  
  99.         }  
  100.   
  101.         nth_element(dp[now].N,dp[now].N+dp[now].size-1,dp[now].N+dp[now].size);  
  102.         printf("%d\n",dp[now].N[dp[now].size-1]);  
  103.     }  
  104. }  


HDU 1693 Eat the Trees

    这题开始,就是真正的插头DP了。要搞清楚插头的概念,仍然建议看2008年国家集训队CDQ的论文:

    这题也没有那么难。每个插头两种状态,有或者无。用0,1记录即可,按照轮廓线逐格递推即可。代码如下:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5.   
  6. long long dp[2][1<<12];  
  7.   
  8. int main()  
  9. {  
  10.     int T;  
  11.     int cas=1;  
  12.     scanf("%d",&T);  
  13.     while(T--)  
  14.     {  
  15.         int n,m;  
  16.         scanf("%d%d",&n,&m);  
  17.         int now=1;  
  18.         int pre=0;  
  19.         int total=1<<(m+1);  
  20.         memset(dp[now],0,sizeof(dp[now]));  
  21.         dp[now][0]=1;  
  22.   
  23.         for(int i=0;i<n;i++)  
  24.         {  
  25.             for(int j=0;j<m;j++)  
  26.             {  
  27.                 int v;  
  28.                 scanf("%d",&v);  
  29.   
  30.                 swap(now,pre);  
  31.                 memset(dp[now],0,sizeof(dp[now]));  
  32.   
  33.                 int j0=1<<j;  
  34.                 int j1=j0<<1;  
  35.   
  36.                 for(int S=0;S<total;S++) if(dp[pre][S])  
  37.                 {  
  38.                     if(v==0)  
  39.                     {  
  40.                         if( (S&j0)==0 && (S&j1)==0 )  
  41.                             dp[now][S]+=dp[pre][S];  
  42.                         continue;  
  43.                     }  
  44.   
  45.                     dp[now][S^j0^j1]+=dp[pre][S];  
  46.   
  47.                     if( ((S&j0)!=0)^((S&j1)!=0) )  
  48.                     {  
  49.                         dp[now][S]+=dp[pre][S];  
  50.                     }  
  51.                 }  
  52.             }  
  53.   
  54.             swap(now,pre);  
  55.             memset(dp[now],0,sizeof(dp[now]));  
  56.             for(int S=0;S<total/2;S++) if(dp[pre][S])  
  57.             {  
  58.                 dp[now][(S<<1)&(total-1)]+=dp[pre][S]; // new line  
  59.             }  
  60.         }  
  61.         printf("Case %d: There are %I64d ways to eat the trees.\n",cas++,dp[now][0]);  
  62.     }  
  63. }  
    模板化代码:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. #include <iostream>  
  5. using namespace std;  
  6. #define LL long long   
  7. const int maxn=2053;  
  8. int maze[16][16];  
  9.   
  10. struct Node  
  11. {  
  12.     int H[maxn];  
  13.     int S[maxn];  
  14.     LL N[maxn];  
  15.     int size;  
  16.     void init()  
  17.     {  
  18.         size=0;  
  19.         memset(H,-1,sizeof(H));  
  20.     }  
  21.   
  22.     void push(int SS,LL num)  
  23.     {  
  24.         int s=SS%maxn;  
  25.         while( ~H[s] && S[H[s]]!=SS )  
  26.         {  
  27.             s++;  
  28.             s%=maxn;  
  29.         }  
  30.   
  31.         if(~H[s])  
  32.         {  
  33.             N[H[s]]+=num;  
  34.             return;  
  35.         }  
  36.   
  37.         N[size]=num;  
  38.         S[size]=SS;  
  39.         H[s]=size++;  
  40.     }  
  41.   
  42.     LL get(int SS)  
  43.     {  
  44.         int s=SS%maxn;  
  45.         while( ~H[s] && S[H[s]]!=SS)  
  46.         {  
  47.             s++;  
  48.             s%=maxn;  
  49.         }  
  50.   
  51.         if(~H[s])  
  52.             return N[H[s]];  
  53.         else  
  54.             return 0;  
  55.     }  
  56. } dp[2];  
  57. int now,pre;  
  58.   
  59. int get(int S,int p,int l=1)  
  60. {  
  61.     return (S>>(p*l))&((1<<l)-1);  
  62. }  
  63.   
  64. void set(int &S,int p,int v,int l=1)  
  65. {  
  66.     S^=get(S,p,l)<<(p*l);  
  67.     S^=(v&((1<<l)-1))<<(p*l);  
  68. }  
  69.   
  70. int main()  
  71. {  
  72.     int T;  
  73.     int cas=1;  
  74.     scanf("%d",&T);  
  75.     while(T--)  
  76.     {  
  77.         int n,m;  
  78.         scanf("%d%d",&n,&m);  
  79.         for(int i=0;i<n;i++)  
  80.             for(int j=0;j<m;j++)  
  81.                 scanf("%d",&maze[i][j]);  
  82.   
  83.         now=1,pre=0;  
  84.         dp[now].init();  
  85.         dp[now].push(0,1);  
  86.   
  87.         for(int i=0;i<n;i++)  
  88.         {  
  89.             for(int j=0;j<m;j++)  
  90.             {  
  91.                 swap(now,pre);  
  92.                 dp[now].init();  
  93.   
  94.                 for(int s=0;s<dp[pre].size;s++)  
  95.                 {  
  96.                     int S=dp[pre].S[s];  
  97.                     LL num=dp[pre].N[s];  
  98.                     int p=get(S,j);  
  99.                     int q=get(S,j+1);  
  100.   
  101.                     if(maze[i][j]==0)  
  102.                     {  
  103.                         if(p==0 && q==0)  
  104.                             dp[now].push(S,num);  
  105.                         continue;  
  106.                     }  
  107.   
  108.                     if(p==0 && q==0)  
  109.                     {  
  110.                         if(maze[i][j+1] && maze[i+1][j])  
  111.                         {  
  112.                             set(S,j,1);  
  113.                             set(S,j+1,1);  
  114.                             dp[now].push(S,num);  
  115.                         }  
  116.                     }  
  117.                     else if(p^q)  
  118.                     {  
  119.                         if(maze[i+p][j+q])  
  120.                             dp[now].push(S,num);  
  121.                         set(S,j,q);  
  122.                         set(S,j+1,p);  
  123.                         if(maze[i+q][j+p])  
  124.                             dp[now].push(S,num);  
  125.                     }  
  126.                     else  
  127.                     {  
  128.                         set(S,j,0);  
  129.                         set(S,j+1,0);  
  130.                         dp[now].push(S,num);  
  131.                     }  
  132.                 }  
  133.             }  
  134.   
  135.             for(int s=0;s<dp[now].size;s++)  
  136.                 dp[now].S[s]<<=1;  
  137.         }  
  138.   
  139.         printf("Case %d: There are %I64d ways to eat the trees.\n",cas++,dp[now].get(0));  
  140.     }  
  141. }  


Ural 1519 Formula 1

    终于到了插头DP的入门题了。如果看了CDQ的PPT,又完成了上面这题,做出这题还是可以的。这里推荐看另外一篇博文:http://blog.sina.com.cn/s/blog_51cea4040100gmky.html。文中的图很详细,也有代码(虽然直接提交代码A不了= =)。
    这题我也搞了很久,看了很多其他人的代码。我的代码如下:
[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <iostream>  
  4. #include <map>  
  5. #include <algorithm>  
  6. using namespace std;  
  7.   
  8. #define LL long long  
  9. LL dp[2][1<<24];  
  10. int state[2][1<<24];  
  11. int top[2];  
  12. int now,pre;  
  13. int endx,endy;  
  14. bool maze[15][15];  
  15. int m,n;  
  16. LL ans;  
  17. const int HASH = 1000037;  
  18. int Hash[HASH];  
  19. int save[HASH];  
  20.   
  21. void HashIn(int S,LL num)  
  22. {  
  23.     int s=S%HASH;  
  24.     while(~Hash[s] && save[s]!=S)  
  25.     {  
  26.         s++;  
  27.         s%=HASH;  
  28.     }  
  29.   
  30.     if(Hash[s]==-1)  
  31.     {  
  32.         dp[now][top[now]]=num;  
  33.         state[now][top[now]]=S;  
  34.         Hash[s]=top[now];  
  35.         save[s]=S;  
  36.         top[now]++;  
  37.     }  
  38.     else  
  39.     {  
  40.         dp[now][Hash[s]]+=num;  
  41.     }  
  42. }  
  43.   
  44. void init()  
  45. {  
  46.     memset(maze,0,sizeof(maze));  
  47.     endx=-1;  
  48.     for(int i=0;i<n;i++)  
  49.     {  
  50.         char str[200];  
  51.         memset(str,0,sizeof(str));  
  52.         scanf("%s",str);  
  53.         for(int j=0;j<m;j++)  
  54.         {  
  55.             if(str[j]=='*')  
  56.             {  
  57.                 maze[i][j]=0;  
  58.             }  
  59.             else if(str[j]=='.')  
  60.             {  
  61.                 maze[i][j]=1;  
  62.                 endx=i;  
  63.                 endy=j;  
  64.             }  
  65.         }  
  66.     }  
  67. }  
  68.   
  69. //  位运算,取S按长度l的第p位  
  70. int getV(int S,int p,int l=2)  
  71. {  
  72.     return (S>>(p*l))&((1<<l)-1);  
  73. }  
  74.   
  75. //  位运算,设置S按长度l的第p位值为v  
  76. void setV(int& S,int p,int v,int l=2)  
  77. {  
  78.     S^=getV(S,p)<<(p*l);  
  79.     S|=v<<(p*l);  
  80. }  
  81.   
  82. void memsetnow()  
  83. {  
  84.     memset(Hash,-1,sizeof(Hash));  
  85.     top[now]=0;  
  86. }  
  87.   
  88. void solve()  
  89. {  
  90.     init();  
  91.     if(endx==-1)  
  92.     {  
  93.         puts("0");  
  94.         return;  
  95.     }  
  96.   
  97.     now=1;  
  98.     pre=0;  
  99.     ans=0;  
  100.     memsetnow();  
  101.     dp[now][0]=1;  
  102.     state[now][0]=0;  
  103.     top[now]=1;  
  104.   
  105.     for(int i=0;i<n;i++)  
  106.     {  
  107.         for(int j=0;j<m;j++)  
  108.         {  
  109.             int j2=j+j;  
  110.             int j22=j2+2;  
  111.             int j0=(1<<j2);  
  112.             j0|=j0<<1;  
  113.             int j1=j0<<2;  
  114.   
  115.             swap(now,pre);  
  116.             memsetnow();  
  117.   
  118.             for(int s=top[pre]-1;s>=0;s--) if(dp[pre][s])  
  119.             {  
  120.   
  121.                 LL num=dp[pre][s];  
  122.                 int S=state[pre][s];  
  123.                 int p=getV(S,j);  
  124.                 int q=getV(S,j+1);  
  125.   
  126.                 if(maze[i][j]==0)  
  127.                 {  
  128.                     if(p==0 && q==0)  
  129.                     {  
  130.                         HashIn(S,num);  
  131.                     }  
  132.                     continue;  
  133.                 }  
  134.   
  135.                 if( (p>0) ^ (q>0) )  
  136.                 {  
  137.                     if(maze[i+(p>0)][j+(q>0)])  
  138.                     {  
  139.                         HashIn(S,num);  
  140.                     }  
  141.                     if(maze[i+(q>0)][j+(p>0)])  
  142.                     {  
  143.                         int nS=S;  
  144.                         setV(nS,j,q);  
  145.                         setV(nS,j+1,p);  
  146.                         HashIn(nS,num);  
  147.                     }  
  148.                 }  
  149.                 else if(p==0 && q==0)  
  150.                 {  
  151.                     if(maze[i+1][j]&&maze[i][j+1])  
  152.                     {  
  153.                         int nS=S;  
  154.                         setV(nS,j,1);  
  155.                         setV(nS,j+1,2);  
  156.                         HashIn(nS,num);  
  157.                     }  
  158.                 }  
  159.                 else if(p==1 && q==1)  
  160.                 {  
  161.                     int find=1;  
  162.                     for(int l=j+2;l<=m;l++)  
  163.                     {  
  164.                         int vv=getV(S,l);  
  165.                         if(vv==1)  
  166.                             find++;  
  167.                         else if(vv==2)  
  168.                             find--;  
  169.   
  170.                         if(find==0)  
  171.                         {  
  172.                             int nS=S;  
  173.                             setV(nS,j,0);  
  174.                             setV(nS,j+1,0);  
  175.                             setV(nS,l,1);  
  176.                             HashIn(nS,num);  
  177.                             break;  
  178.                         }  
  179.                     }  
  180.                 }  
  181.                 else if(p==2 && q==2)  
  182.                 {  
  183.                     int find=1;  
  184.                     for(int l=j-1;l>=0;l--)  
  185.                     {  
  186.                         int vv=getV(S,l);  
  187.                         if(vv==2)  
  188.                             find++;  
  189.                         else if(vv==1)  
  190.                             find--;  
  191.   
  192.                         if(find==0)  
  193.                         {  
  194.                             int nS=S;  
  195.                             setV(nS,j,0);  
  196.                             setV(nS,j+1,0);  
  197.                             setV(nS,l,2);  
  198.                             HashIn(nS,num);  
  199.                             break;  
  200.                         }  
  201.                     }  
  202.                 }  
  203.                 else if(p==2 && q==1)  
  204.                 {  
  205.                     int nS=S;  
  206.                     setV(nS,j,0);  
  207.                     setV(nS,j+1,0);  
  208.                     HashIn(nS,num);  
  209.                 }  
  210.                 else if(p==1 && q==2)  
  211.                 {  
  212.                     if(i==endx && j==endy)  
  213.                         ans+=num;  
  214.                 }  
  215.             }  
  216.         }  
  217.   
  218.         swap(now,pre);  
  219.         memsetnow();  
  220.         for(int s=0;s<top[pre];s++) if(dp[pre][s])  
  221.         {  
  222.             LL num=dp[pre][s];  
  223.             int S=state[pre][s]<<2;  
  224.             HashIn(S,num);  
  225.         }  
  226.     }  
  227.   
  228.     printf("%I64d\n",ans);  
  229. }  
  230.   
  231. int main()  
  232. {  
  233.     while(~scanf("%d%d",&n,&m))  
  234.     {  
  235.         solve();  
  236.     }  
  237. }  
模板化代码如下:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <iostream>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define LL long long  
  7.   
  8. const int maxn=100037;  // 可能的最大状态数  
  9. struct Node  
  10. {  
  11.     int H[maxn];    // 哈希  
  12.     int S[maxn];    // 状态  
  13.     LL N[maxn];     // 状态对应的数量  
  14.     int size;       // 总的状态数量  
  15.     void init()     // 初始化  
  16.     {  
  17.         memset(H,-1,sizeof(H));  
  18.         size=0;  
  19.     }  
  20.     void push(int s,LL num) // 将状态压入,根据哈希的结果建立新状态或加在原有的状态上  
  21.     {  
  22.         int ss=s%maxn;  
  23.         while( ~H[ss] && S[H[ss]]!=s )  
  24.         {  
  25.             ss++;  
  26.             ss%=maxn;  
  27.         }  
  28.   
  29.         if(H[ss]==-1)  
  30.         {  
  31.             S[size]=s;  
  32.             N[size]=num;  
  33.             H[ss]=size++;  
  34.         }  
  35.         else  
  36.         {  
  37.             N[H[ss]]+=num;  
  38.         }  
  39.     }  
  40. } dp[2];  
  41. int now,pre;  
  42. bool maze[13][13];  
  43. int endx,endy;  // 记录最后一个可行位置  
  44. int m,n;  
  45. LL ans;  
  46.   
  47. // 取S状态的第p位,每位l(不是1)个bit  
  48. int get(int S,int p,int l=2)  
  49. {  
  50.     return (S>>(p*l))&((1<<l)-1);  
  51. }  
  52.   
  53. // 置S状态的第p位为v,每位l(不是1)个bit  
  54. void set(int &S,int p,int v,int l=2)  
  55. {  
  56.     S^=get(S,p,l)<<(p*l);  
  57.     S^=(v&((1<<l)-1))<<(p*l);  
  58. }  
  59.   
  60. // 输入地图  
  61. void input()  
  62. {  
  63.     memset(maze,0,sizeof(maze));  
  64.     endx=-1;  
  65.   
  66.     for(int i=0;i<n;i++)  
  67.     {  
  68.         char str[20];  
  69.         scanf("%s",str);  
  70.         for(int j=0;j<m;j++)  
  71.         {  
  72.             if(str[j]=='*')  
  73.             {  
  74.                 maze[i][j] = false;  
  75.             }  
  76.             else if(str[j]=='.')  
  77.             {  
  78.                 maze[i][j]=true;  
  79.                 endx=i;  
  80.                 endy=j;  
  81.             }  
  82.         }  
  83.     }  
  84. }  
  85.   
  86. void solve()  
  87. {  
  88.     now=1;  
  89.     pre=0;  
  90.     ans=0;  
  91.     dp[now].init();  
  92.     dp[now].push(0,1);  
  93.   
  94.     for(int i=0;i<n;i++)  
  95.     {  
  96.         for(int j=0;j<m;j++)  
  97.         {  
  98.             swap(now,pre);  
  99.             dp[now].init();  
  100.   
  101.             for(int s=0;s<dp[pre].size;s++)  
  102.             {  
  103.                 int S=dp[pre].S[s];  
  104.                 LL num=dp[pre].N[s];  
  105.                 int p=get(S,j);  
  106.                 int q=get(S,j+1);  
  107.   
  108.                 if(maze[i][j]==0)   // 地图中不可走的点  
  109.                 {  
  110.                     if(p==0 && q==0)  
  111.                     {  
  112.                         dp[now].push(S,num);  
  113.                     }  
  114.                     continue;  
  115.                 }  
  116.   
  117.                 if(p==0 && q==0)    // 如果地图允许,构造新的连通块  
  118.                 {  
  119.                     if(maze[i+1][j] && maze[i][j+1])  
  120.                     {  
  121.                         set(S,j,1);  
  122.                         set(S,j+1,2);  
  123.                         dp[now].push(S,num);  
  124.                     }  
  125.                 }  
  126.                 else if( (p>0)^(q>0) )    // 左边和右边有一边有插头  
  127.                 {  
  128.                     if(maze[i+(p>0)][j+(q>0)])  
  129.                     {  
  130.                         dp[now].push(S,num);  
  131.                     }  
  132.                     if(maze[i+(q>0)][j+(p>0)])  
  133.                     {  
  134.                         set(S,j,q);  
  135.                         set(S,j+1,p);  
  136.                         dp[now].push(S,num);  
  137.                     }  
  138.                 }  
  139.                 else if( p==2 && q==1 ) // 结束连通块  
  140.                 {  
  141.                     set(S,j,0);  
  142.                     set(S,j+1,0);  
  143.                     dp[now].push(S,num);  
  144.                 }  
  145.                 else if( p==1 && q==1 ) // 寻找对应的2插头  
  146.                 {  
  147.                     int find=1;  
  148.                     for(int k=j+2;k<=m;k++)  
  149.                     {  
  150.                         int v=get(S,k);  
  151.                         if(v==2)  
  152.                             find--;  
  153.                         else if(v==1)  
  154.                             find++;  
  155.                         if(find==0)  
  156.                         {  
  157.                             set(S,j,0);  
  158.                             set(S,j+1,0);  
  159.                             set(S,k,1);  
  160.                             dp[now].push(S,num);  
  161.                             break;  
  162.                         }  
  163.                     }  
  164.                 }  
  165.                 else if( p==2 && q==2 ) // 寻找对应的1插头  
  166.                 {  
  167.                     int find=1;  
  168.                     for(int k=j-1;k>=0;k--)  
  169.                     {  
  170.                         int v=get(S,k);  
  171.                         if(v==1)  
  172.                             find--;  
  173.                         else if(v==2)  
  174.                             find++;  
  175.                         if(find==0)  
  176.                         {  
  177.                             set(S,j,0);  
  178.                             set(S,j+1,0);  
  179.                             set(S,k,2);  
  180.                             dp[now].push(S,num);  
  181.                             break;  
  182.                         }  
  183.                     }  
  184.                 }  
  185.                 else if( p==1 && q==2 ) // 结束  
  186.                 {  
  187.                     if(i==endx && j==endy)  
  188.                         ans+=num;  
  189.                 }  
  190.             }  
  191.         }  
  192.   
  193.         for(int s=0;s<dp[now].size;s++)  // 换行  
  194.             dp[now].S[s]<<=2;  
  195.     }  
  196. }  
  197.   
  198. int main()  
  199. {  
  200.     while(~scanf("%d%d",&n,&m))  
  201.     {  
  202.         input();  
  203.         if(endx==-1) {puts("0");continue;}  
  204.         solve();  
  205.         printf("%I64d\n",ans);  
  206.     }  
  207. }  

    只前的模板一直都是用括号法。时间空间上都要比最小表示法好。无奈对于广义路径,括号序列就力不从心了。本题最小表示法如下:

[cpp] view plain copy
 print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5. typedef long long LL;  
  6. const int maxn=29989;  
  7. const int L=3;  
  8. int now,pre;  
  9. bool maze[15][15];  
  10. int endx,endy;  
  11. int code[15],ch[15];  
  12. int n,m;  
  13. LL ans;  
  14.   
  15. struct Node  
  16. {  
  17.     int H[maxn];  
  18.     LL S[maxn];  
  19.     LL N[maxn];  
  20.     int size;  
  21.     void init()  
  22.     {  
  23.         memset(H,-1,sizeof(H));  
  24.         size=0;  
  25.     }  
  26.     void push(LL SS,LL num)  
  27.     {  
  28.         int s=SS%maxn;  
  29.         while( ~H[s] && S[H[s]]!=SS )  
  30.             s=(s+1)%maxn;  
  31.         if( ~H[s] )  
  32.         {  
  33.             N[H[s]]+=num;  
  34.         }  
  35.         else  
  36.         {  
  37.             S[size]=SS;  
  38.             N[size]=num;  
  39.             H[s]=size++;  
  40.         }  
  41.     }  
  42. } dp[2];  
  43.   
  44. LL encode()  
  45. {  
  46.     memset(ch,-1,sizeof(ch));  
  47.     ch[0]=0;  
  48.   
  49.     int cnt=1;  
  50.     LL S=0;  
  51.     for(int i=m;i>=0;i--)  
  52.     {  
  53.         if(ch[code[i]]==-1)  
  54.             ch[code[i]]=cnt++;  
  55.         code[i]=ch[code[i]];  
  56.         S<<=L;  
  57.         S|=code[i];  
  58.     }  
  59.     return S;  
  60. }  
  61.   
  62. void decode(LL S)  
  63. {  
  64.     for(int i=0;i<=m;i++)  
  65.     {  
  66.         code[i]=S&((1<<L)-1);  
  67.         S>>=L;  
  68.     }  
  69. }  
  70.   
  71. void shift()  
  72. {  
  73.     for(int s=0;s<dp[now].size;s++)  
  74.         dp[now].S[s]<<=L;  
  75. }  
  76.   
  77. void doGrid(int i,int j)  
  78. {  
  79.     for(int s=0;s<dp[pre].size;s++)  
  80.     {  
  81.         LL S=dp[pre].S[s];  
  82.         LL N=dp[pre].N[s];  
  83.         decode(S);  
  84.         int left=code[j];  
  85.         int up=code[j+1];  
  86.         int mi=min(left,up);  
  87.         int ma=max(left,up);  
  88.   
  89.         if(maze[i][j]==0)  
  90.         {  
  91.             if(ma==0) dp[now].push(encode(),N);  
  92.             continue;  
  93.         }  
  94.   
  95.         if(ma==0)  
  96.         {  
  97.             if(maze[i][j+1] && maze[i+1][j])  
  98.             {  
  99.                 code[j]=code[j+1]=13;  
  100.                 dp[now].push(encode(),N);  
  101.             }  
  102.         }  
  103.         else if(mi==0)  
  104.         {  
  105.             if(maze[i+1][j])  
  106.             {  
  107.                 code[j]=ma;  
  108.                 code[j+1]=0;  
  109.                 dp[now].push(encode(),N);  
  110.             }  
  111.             if(maze[i][j+1])  
  112.             {  
  113.                 code[j]=0;  
  114.                 code[j+1]=ma;  
  115.                 dp[now].push(encode(),N);  
  116.             }  
  117.         }  
  118.         else if(left==up)  
  119.         {  
  120.             if(i==endx && j==endy)  
  121.                 ans+=N;  
  122.         }  
  123.         else  
  124.         {  
  125.             code[j]=code[j+1]=0;  
  126.             for(int k=0;k<=m;k++)  
  127.                 if(code[k]==up)  
  128.                     code[k]=left;  
  129.             dp[now].push(encode(),N);  
  130.         }  
  131.     }  
  132. }  
  133.   
  134. void solve()  
  135. {  
  136.     now=1;  
  137.     pre=0;  
  138.     ans=0;  
  139.     dp[now].init();  
  140.     dp[now].push(0,1);  
  141.   
  142.     for(int i=0;i<n;i++)  
  143.     {  
  144.         for(int j=0;j<m;j++)  
  145.         {  
  146.             swap(now,pre);  
  147.             dp[now].init();  
  148.             doGrid(i,j);  
  149.         }  
  150.         shift();  
  151.     }  
  152. }  
  153.   
  154. void init()  
  155. {  
  156.     memset(maze,0,sizeof(maze));  
  157.     endx=-1;  
  158.   
  159.     char str[20];  
  160.     for(int i=0;i<n;i++)  
  161.     {  
  162.         scanf("%s",str);  
  163.         for(int j=0;j<m;j++) if(str[j]=='.')  
  164.             maze[i][j]=1,endx=i,endy=j;  
  165.     }  
  166. }  
  167.   
  168. int main()  
  169. {  
  170.     while(~scanf("%d%d",&n,&m))  
  171.     {  
  172.         init();  
  173.         if(endx==-1) {puts("0");continue;}  
  174.         solve();  
  175.         printf("%I64d\n",ans);  
  176.     }  
  177. }  
    最小表示法,简单来说就是数字相同代表连通。每次通过decode读取状态,通过encode将状态转为二进制数。因为12*12的棋盘最多只有6对插头,所以使用3进制(7个插头)。


结束?

    好了,入门结束了,插头DP才刚开始。接下来推荐刷Kuangbin大神的题表:http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710343.html。大家A的开心。

    插头DP的广义路径建议看CDQ的论文(非PPT),以及NotOnlySuccess的博客:http://www.notonlysuccess.com/index.php/plug-dp-complete/

    转载注明出处:SF-_-: http://blog.csdn.net/sf____


原创粉丝点击