其他分享
首页 > 其他分享> > 插头 dp 总结

插头 dp 总结

作者:互联网

tip:

  

实战:

T1:Ural 1519 Formula 1

题干:

  一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数

题解:

  所谓‘哈密顿回路’,就是不相交的欧拉回路。

  一道插头 dp 的裸题。

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #define int long long
 5 #define $ 40000000
 6 #define hash 299987
 7 using namespace std;
 8 int m,n,a[15][15],nxt[$],first[300000];
 9 int h[16],ans,self[2][$],now,last,tot[2],val[2][$],s,t;
10 inline void add(int x,int add){
11     int tmp=x%hash+1;
12     for(register int i=first[tmp];i;i=nxt[i])
13         if(self[now][i]==x){    val[now][i]+=add; return;    }
14     nxt[++tot[now]]=first[tmp];
15     first[tmp]=tot[now];
16     self[now][tot[now]]=x;
17     val[now][tot[now]]=add;
18 }
19 signed main(){
20     scanf("%lld%lld",&n,&m);
21     for(register int i=1;i<=n;++i)
22         for(register int j=1;j<=m;++j){
23             char ch=getchar();
24             while(ch!='*'&&ch!='.') ch=getchar();
25             if(ch=='.') a[i][j]=1, s=i, t=j;
26         }
27     h[0]=1;
28     for(register int i=1;i<=15;++i) h[i]=h[i-1]<<2;
29     tot[now]=1, val[now][1]=1, self[now][1]=0;
30     for(register int i=1;i<=n;++i){
31         for(register int j=1;j<=tot[now];++j) self[now][j]<<=2;
32         for(register int j=1;j<=m;++j){
33             memset(first,0,sizeof(first));
34             last=now, now^=1;   tot[now]=0;
35             for(register int k=1;k<=tot[last];++k){
36                 int tmp=self[last][k], w=val[last][k];
37                 int p1=(tmp>>((j-1)*2))%4, p2=(tmp>>(j*2))%4;
38                 if(!a[i][j]){    if(!p1&&!p2)  add(tmp,w);    }
39                 else if(!p1&&!p2){
40                     if(a[i+1][j]&&a[i][j+1]) add(tmp+h[j-1]+h[j]*2,w);
41                 }
42                 else if(!p1&&p2){
43                     if(a[i][j+1])  add(tmp,w);
44                     if(a[i+1][j])  add(tmp-h[j]*p2+h[j-1]*p2,w);
45                 }
46                 else if(p1&&!p2){
47                     if(a[i+1][j])  add(tmp,w);
48                     if(a[i][j+1])  add(tmp-h[j-1]*p1+h[j]*p1,w);
49                 }
50                 else if(p1==1&&p2==1){
51                     int tip=1;
52                     for(register int l=j+1;l<=m;++l){
53                         if((tmp>>(l*2))%4==1) ++tip;
54                         if((tmp>>(l*2))%4==2) --tip;
55                         if(tip==0){
56                             add(tmp-h[j]-h[j-1]-h[l],w); break;
57                         }
58                     }
59                 }
60                 else if(p1==2&&p2==2){
61                     int tip=1;
62                     for(register int l=j-2;l>=0;--l){
63                         if((tmp>>(l*2))%4==1) --tip;
64                         if((tmp>>(l*2))%4==2) ++tip;
65                         if(tip==0){
66                             add(tmp-h[j]*2-h[j-1]*2+h[l],w); break;
67                         }
68                     }
69                 }
70                 else if(p1==2&&p2==1) add(tmp-h[j-1]*2-h[j],w);
71                 else if(p1==1&&p2==2&&i==s&&j==t)  ans+=w;
72             }
73         }
74     }
75     printf("%lld\n",ans);
76 }
View Code

 

T2:邮递员

题干:

  Smith 在 P 市的邮政局工作,他每天的工作是从邮局出发,到自己所管辖的所有邮筒取信件,然后带回邮局。他所管辖的邮筒非常巧地排成了一个 m * n 的点阵(点阵中的间距都是相等的)。左上角的邮筒恰好在邮局的门口。  Smith 是一个非常标新立异的人,他希望每天都能走不同的路线,但是同时,他又不希望路线的长度增加,他想知道他有多少条不同的路线可走。

  你的程序需要根据给定的输入,给出符合题意的输出:输入包括点阵的 m 和 n 的值;你需要根据给出的输入,计算出 Smith 可选的不同路线的总条数;

题解:

  

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #define $ 500100
 5 #define hash 299987
 6 using namespace std;
 7 int n,m,s,t,now,last,self[2][$],nxt[$],tot[2],h[15],first[hash+10],a[21][11];
 8 struct tree{
 9     int a[22];
10     tree(){    memset(a,0,sizeof(a));    }
11     friend tree operator + (const tree &a,const tree &b){
12         tree c=tree();
13         int yu=0,adda=0,addb=0;
14         for(register int i=1;i<=a.a[0]||i<=b.a[0]||yu>0;++i,adda=0,addb=0){
15             if(i<=a.a[0]) adda=a.a[i];
16             if(i<=b.a[0]) addb=b.a[i];
17             c.a[i]=adda+addb+yu;
18             yu=c.a[i]/10000; c.a[i]%=10000; 
19             c.a[0]=i;
20         }
21         return c;
22     }
23     inline void print(){
24         printf("%d",a[a[0]]);
25         for(register int i=a[0]-1;i>=1;--i) printf("%04d",a[i]);
26     }
27 }ans,val[2][$],w;
28 inline void swap(int &x,int &y){   int t=y; y=x; x=t;    }
29 inline int min(int x,int y){    return x<y?x:y;    }
30 inline void add(const int &x,const tree &add){
31     int tmp=x%hash+1;
32     for(register int i=first[tmp];i;i=nxt[i])
33         if(self[now][i]==x){    val[now][i]=val[now][i]+add; return ;    }
34     nxt[++tot[now]]=first[tmp];
35     first[tmp]=tot[now];
36     val[now][tot[now]]=add;
37     self[now][tot[now]]=x;
38 }
39 signed main(){
40     h[0]=1;
41     for(register int i=1;i<=12;++i) h[i]=h[i-1]<<2;
42     scanf("%d%d",&n,&m);
43     if(n<m) swap(n,m);
44     if(min(m,n)==1){    puts("1"); return 0;    }
45      for(register int i=1;i<=n;++i)
46         for(register int j=1;j<=m;++j) a[i][j]=1;
47     w.a[0]=w.a[1]=1; add(0,w);
48     for(register int i=1;i<=n;++i){
49         for(register int j=1;j<=tot[now];++j) self[now][j]<<=2;
50         for(register int j=1;j<=m;++j){
51             memset(first,0,sizeof(first));
52             last=now, now^=1, tot[now]=0;
53             for(register int k=1;k<=tot[last];++k){
54                 int tmp=self[last][k], p1=(tmp>>((j-1)*2))%4, p2=(tmp>>(j*2))%4;
55                 w=val[last][k];
56                 if(!a[i][j]){    if(!p1&&!p2) add(tmp,w);    }
57                 else if(!p1&&!p2){
58                     if(a[i+1][j]&&a[i][j+1]) add(tmp+h[j-1]+h[j]*2,w);
59                 }
60                 else if(!p1&&p2){
61                     if(a[i][j+1]) add(tmp,w);
62                     if(a[i+1][j]) add(tmp-p2*h[j]+p2*h[j-1],w);
63                 }
64                 else if(p1&&!p2){
65                     if(a[i+1][j]) add(tmp,w);
66                     if(a[i][j+1]) add(tmp-p1*h[j-1]+p1*h[j],w);
67                 }
68                 else if(p1==1&&p2==1){
69                     int tip=1;
70                     for(register int l=j+1;l<=m;++l){
71                         if((tmp>>(l*2))%4==1) ++tip;
72                         if((tmp>>(l*2))%4==2) --tip;
73                         if(tip==0){    add(tmp-h[j-1]*p1-h[j]*p2-h[l],w); break;    }
74                     }
75                 }
76                 else if(p1==2&&p2==2){
77                     int tip=1;
78                     for(register int l=j-2;l>=0;--l){
79                         if((tmp>>(l*2))%4==1) --tip;
80                         if((tmp>>(l*2))%4==2) ++tip;
81                         if(tip==0){    add(tmp-p1*h[j-1]-p1*h[j]+h[l],w); break;    }
82                     }
83                 }
84                 else if(p1==2&&p2==1) add(tmp-h[j-1]*p1-h[j]*p2,w);
85                 else if(p1==1&&p2==2&&i==n&&j==m) ans=ans+w;
86             }
87         }
88     }
89     ans=ans+ans; ans.print();
90 }
View Code

 

T3:CITY

题干:

  小明和小华要参加 NOI,踏上了去 X 市的火车。

  小明望着窗外的田野,大楼,工厂缓缓后退,在思考着什么。

  这时,对面的小华拿出手机对着他说:“看!我们在这个位置!”

  小明望着手机上显示的地图,城市被接到分割成各个方块,而自己所在的点在慢慢移动。

  他突然意识到自己甚至还没游历过这个自己所在的小城市,学校和家貌以及之间来回的道路似乎成了这个小城的唯一印象。

  若我把它们全部走一圈,可能要仔细计划下吧……不,那么多方案,其实我应该早能做到了吧……小明在心里对自己说。

  第一行有两个数 N , M 表示地图被分割成 N * M 个块,接下来有 N 行,每行有 M 个字符。

  输入格式:

  .  表示这个块可以通过

  -  表示这个块只可以左右通过

  |  表示这个块只可以上下通过

  #  表示这个块不能通过

  (从每个块只能走到其上下左右相邻的四个块)

  输出格式:一个数,表示小明把所以可以通过的块都经过且只经过一次并回到原地的方案数。

题解:

 

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #define $ 2000010
 5 #define hash 299987
 6 #define int long long
 7 using namespace std;
 8 int n,m,first[hash+10],nxt[$],val[2][$],self[2][$];
 9 int a[14][14],h[14],now,last,s,t,tot[2],ans;
10 inline void add(int x,int add){
11     int tmp=x%hash+1;
12     for(register int i=first[tmp];i;i=nxt[i])
13         if(self[now][i]==x){    val[now][i]+=add; return;    }
14     nxt[++tot[now]]=first[tmp];
15     first[tmp]=tot[now];
16     val[now][tot[now]]=add;
17     self[now][tot[now]]=x;
18 }
19 signed main(){
20     h[0]=1;
21     for(register int i=1;i<=13;++i) h[i]=h[i-1]<<2;
22     scanf("%lld%lld",&n,&m);
23     for(register int i=1;i<=n;++i)
24         for(register int j=1;j<=m;++j){    
25             char ch=getchar();
26             while(ch!='.'&&ch!='|'&&ch!='-'&&ch!='#') ch=getchar();
27             if(ch=='.') a[i][j]=1, s=i, t=j;
28             if(ch=='-') a[i][j]=2;
29             if(ch=='|') a[i][j]=3;
30         }
31     tot[now]=1, self[now][tot[now]]=0, val[now][tot[now]]=1;
32     for(register int i=1;i<=n;++i){
33         for(register int j=1;j<=tot[now];++j) self[now][j]<<=2;
34         for(register int j=1;j<=m;++j){
35             memset(first,0,sizeof(first));
36             last=now, now^=1, tot[now]=0;
37             for(register int k=1;k<=tot[last];++k){
38                 int tmp=self[last][k], p1=(tmp>>((j-1)*2))&3, p2=(tmp>>(j*2))&3;
39                 int w=val[last][k];
40                 if(a[i][j]==0){
41                     if(!p1&&!p2) add(tmp,w);
42                 }
43                 else if(a[i][j]==3){
44                     if(!p1&&p2&&(a[i+1][j]==1||a[i+1][j]==3)) add(tmp-p2*h[j]+p2*h[j-1],w);
45                 }
46                 else if(a[i][j]==2){
47                     if(p1&&!p2&&(a[i][j+1]==1||a[i][j+1]==2)) add(tmp-p1*h[j-1]+p1*h[j],w); 
48                 }
49                 else if(!p1&&!p2){
50                     if((a[i+1][j]==1||a[i+1][j]==3)&&(a[i][j+1]==1||a[i][j+1]==2)) 
51                         add(tmp+h[j-1]+h[j]*2,w);
52                 }
53                 else if(!p1&&p2){
54                     if(a[i][j+1]==1||a[i][j+1]==2) add(tmp,w); 
55                     if(a[i+1][j]==1||a[i+1][j]==3) add(tmp-p2*h[j]+p2*h[j-1],w);
56                 }
57                 else if(p1&&!p2){
58                     if(a[i+1][j]==1||a[i+1][j]==3) add(tmp,w);
59                     if(a[i][j+1]==1||a[i][j+1]==2) add(tmp-p1*h[j-1]+p1*h[j],w); 
60                 }
61                 else if(p1==1&&p2==1){
62                     int tip=1;
63                     for(register int l=j+1;l<=m;++l){
64                         if(((tmp>>(l*2))&3)==1) ++tip;
65                         if(((tmp>>(l*2))&3)==2) --tip;
66                         if(tip==0){    add(tmp-p1*h[j-1]-p2*h[j]-h[l],w); break;    }
67                     }
68                 }
69                 else if(p1==2&&p2==2){
70                     int tip=1;
71                     for(register int l=j-2;l>=0;--l){
72                         if(((tmp>>(l*2))&3)==1) --tip;
73                         if(((tmp>>(l*2))&3)==2) ++tip;
74                         if(tip==0){    add(tmp-p1*h[j-1]-p2*h[j]+h[l],w); break;    }
75                     }
76                 }
77                 else if(p1==2&&p2==1)  add(tmp-p1*h[j-1]-p2*h[j],w);
78                 else if(p1==1&&p2==2&&i==s&&j==t) ans+=w;
79             }
80         }
81     }
82     printf("%lld",ans);
83 }
View Code

 

T1:

题干:

 

题解:

 

Code:

 

 

T1:

题干:

 

题解:

 

Code:

 

 

标签:插头,总结,p1,int,p2,add,&&,tmp,dp
来源: https://www.cnblogs.com/OI-zzyy/p/11261050.html