這幾天學習了一下插頭DP,刷了11道題。對插頭DP有了點理解。插頭DP就先告一段落吧! 后面還有插頭DP的廣義路徑和其它復雜應用,以后有機會再補上吧!
kuangbin
首先入門推薦的還是cdq的論文:《基於連通性狀態壓縮的動態規划問題》
http://wenku.baidu.com/view/4fe4ac659b6648d7c1c74633.html
上面的論文關於插頭和輪廓線等概念講得很清楚。而且關於插頭DP的精髓也講得很透了。
插頭DP其實就是每格進行狀態轉移。
看懂原理和寫代碼還是有段距離的,可以結合代碼去加深理解原理。同時形成適合自己風格的代碼模板。
HDU 1693 Eat the Trees 此題是比較基礎的入門題。
多條回路,求回路數問題。格子有障礙格子和非障礙格子兩種。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709834.html

#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; const int HASH=10007; const int STATE=1000010;//狀態數 const int MAXD=15; int N,M; int code[MAXD],maze[MAXD][MAXD]; struct HASHMAP { int head[HASH],next[STATE],state[STATE],size; long long f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(int st,long long ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(st==state[i]) { f[i]+=ans; return; } f[size]=ans; state[size]=st; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,int st) { int i; for(i=m;i>=0;i--) { code[i]=st&1; st>>=1; } } int encode(int *code,int m) { int i,st=0; for(int i=0;i<=m;i++) { st<<=1; st|=code[i]; } return st; } void init() { int i,j; scanf("%d%d",&N,&M); for(i=1;i<=N;i++) for(int j=1;j<=M;j++) scanf("%d",&maze[i][j]); for(int i=1;i<=N;i++)maze[i][M+1]=0;//邊界補0 for(int i=1;i<=M;i++)maze[N+1][i]=0; } void shift(int *code,int m)//換行的時候移位 { int i; for(i=m;i>0;i--) code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up)//11 -> 00 { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } else if(left||up)//01 或 10 { if(maze[i][j+1]) { code[j-1]=0; code[j]=1; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j-1]=1; code[j]=0; if(j==M)shift(code,M);//這個不要忘了! hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j]=code[j-1]=1; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } void solve() { int i,j,cur=0; long long ans=0; hm[cur].init(); hm[cur].push(0,1); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } for(i=0;i<hm[cur].size;i++) ans+=hm[cur].f[i]; printf("There are %I64d ways to eat the trees.\n",ans); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; int iCase=0; scanf("%d",&T); while(T--) { iCase++; printf("Case %d: ",iCase); init(); solve(); } return 0; }
Ural 1519 Formula 1 和論文上的同一道題。此題是求單回路數問題,和上面的一題在狀態轉移時有點不同。就是形成回路一定要在最后一個非障礙格子。
我的代碼是用最小表示法做的。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/09/29/2708989.html

/* 最小表示法 */ #include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXD=15; const int HASH=30007; const int STATE=1000010; int N,M; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD];//最小表示法使用 int ex,ey;//最后一個非障礙格子的坐標 struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE]; long long f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,long long ans) { int i; int h=st%HASH; for(i=head[h];i!=-1;i=next[i])//這里要注意是next if(state[i]==st) { f[i]+=ans; return; } state[size]=st; f[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st) { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } long long encode(int *code,int m)//最小表示法 { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up)//只能出現在最后一個非障礙格子 { if(i==ex&&j==ey) { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else//不在同一個連通分量則合並 { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==up) code[t]=left; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else if((left&&(!up))||((!left)&&up)) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j-1]=t; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else//無插頭,則構造新的連通塊 { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } char str[MAXD]; void init() { memset(maze,0,sizeof(maze)); ex=0; for(int i=1;i<=N;i++) { scanf("%s",&str); for(int j=0;j<M;j++) { if(str[j]=='.') { ex=i; ey=j+1; maze[i][j+1]=1; } } } } void solve() { int i,j,cur=0; long long ans=0; hm[cur].init(); hm[cur].push(0,1); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } for(i=0;i<hm[cur].size;i++) ans+=hm[cur].f[i]; printf("%I64d\n",ans); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d%d",&N,&M)!=EOF) { init(); if(ex==0)//沒有空的格子 { printf("0\n"); continue; } solve(); } return 0; }
FZU 1977 Pandora adventure 此題也是單回路數問題。但是格子有了三種:障礙格子,必走格子和選擇性經過格子。這樣導致最后一個非障礙格子不確定。所以增加一個標志位來記錄是否形成回路。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709967.html

/* FZU 1977 簡單回路數問題,N*M(1<=N,M<=12)的方格中有障礙點,必走點和非必走點 因為回路只有一條,而形成回路的最后一個點又是不確定的。 所以額外增加一個標志位來記錄是否形成回路。 如果已經形成回路,而后面又遇到插頭或者必須走的點,則該方案不合法 G++ 375ms */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=100010; int N,M; int code[MAXD]; int maze[MAXD][MAXD];//0表示障礙點,1表示非必走點,2表示必走點 int ch[MAXD]; int isend;//是否形成回路標記位 struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE],f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,long long ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { f[i]+=ans; return; } state[size]=st; f[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st)//注意要增加一個isend標記位 { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } isend=st&1;//isend標記位在st的最高一位 } long long encode(int *code,int m)//增加isend標記位 { long long st=isend;//最高位 int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(isend) {//如果已經形成環路,后面又有插頭或者有必過點,則是非法的 if(left||up||maze[i][j]==2)continue; code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); continue; } if(left&&up) { if(left==up) { code[j-1]=code[j]=0; isend=1; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } else //不在一個連通分量則合並 { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==up) code[t]=left; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else if((left&&(!up))||((!left)&&up)) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j-1]=t; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i][j]==1)//可以經過可以不經過的點 { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } char str[20]; void init() { scanf("%d%d",&N,&M); memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) { scanf("%s",&str); for(int j=1;j<=M;j++) { if(str[j-1]=='*')maze[i][j]=1; else if(str[j-1]=='O')maze[i][j]=2; } } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,1); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } long long ans=0; for(i=0;i<hm[cur].size;i++) ans+=hm[cur].f[i]; printf("%I64d\n",ans); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; int iCase=0; scanf("%d",&T); while(T--) { iCase++; printf("Case %d: ",iCase); init(); solve(); } return 0; }
HDU 1964 Pipes 每個格子之間的牆壁有個花費,求用一個環經過每個格子僅一次的最小花費。此題不求方案數,只需要取最小值即可。而且所有格子都是非障礙格子,這樣和Ural 1519就和類似了,而且更加簡單。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/09/30/2709372.html

/* HDU 1964 Pipes 插頭DP 每個格子之間的牆壁有一個花費,求用一個環經過每個格子一次的最小花費 G++ 46ms */ #include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=1000010; struct Node { int down,right;//每個格子下邊和右邊牆的花費 }node[MAXD][MAXD]; int N,M; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD];//最小表示法使用 int ex,ey;//最后一個非障礙格子的坐標 struct HASHMAP { int head[HASH],next[STATE],size; int dp[STATE]; long long state[STATE];//最小表示法,最大是8^11,就是2^33,所以用long long void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,int ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { if(dp[i]>ans)dp[i]=ans; return; } dp[size]=ans; state[size]=st; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st) { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } long long encode(int *code,int m) { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up)//只出現在最后一個格子 { if(i==ex&&j==ey) { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]); } } else { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==left) code[t]=up; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]); } } else if((left&&(!up))||((!left)&&up)) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+node[i][j].right); } if(maze[i+1][j]) { code[j-1]=t; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+node[i][j].down); } } else//無插頭 { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+node[i][j].down+node[i][j].right); } } } } char str[30]; void init() { scanf("%d%d%*c",&N,&M);//跳過一個字符 memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) maze[i][j]=1; gets(str); for(int i=1;i<N;i++) { gets(str); for(int j=1;j<M;j++) node[i][j].right=str[2*j]-'0'; gets(str); for(int j=1;j<=M;j++) node[i][j].down=str[2*j-1]-'0'; } gets(str); for(int j=1;j<M;j++) node[N][j].right=str[2*j]-'0'; gets(str); ex=N; ey=M; } void solve() { int i,j,cur=0; int ans=0; hm[cur].init(); hm[cur].push(0,0); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); dpblank(i,j,cur); cur^=1; } for(i=0;i<hm[cur].size;i++) ans+=hm[cur].dp[i]; printf("%d\n",ans); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { init(); solve(); } return 0; }
HDU 3377 Plan 從左上角走到右下角,每個格子有個分數。每個格子最多經過一次,可以不經過,求最大分數。 模板修改下就出來了。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709773.html

/* HDU 3377 從左上角走到右下角。每個格子有個分數。 每個格子只能經過一次,可以不經過 求最大分數 G++ 140ms */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=1000010; int N,M; int maze[MAXD][MAXD]; int score[MAXD][MAXD]; int code[MAXD]; int ch[MAXD]; struct HASHMAP { int head[HASH],state[STATE],next[STATE],size; int dp[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(int st,int ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { if(dp[i]<ans)dp[i]=ans; return; } state[size]=st; dp[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,int st) { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } int encode(int *code,int m) { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; int st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if((i==1&&j==1)||(i==N&&j==M)) { if((left&&(!up))||((!left)&&up)) { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } else if(left==0&&up==0) { if(maze[i][j+1]) { code[j-1]=0; code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } if(maze[i][j+1]) { code[j-1]=13; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } } continue; } if(left&&up) { if(left==up)//沒有這種情況,因為不形成環 { } else { code[j-1]=code[j]=0; for(int t=0;t<=M;t++)//這里少了個等號,查了好久的錯 if(code[t]==up) code[t]=left; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } } else if((left&&(!up))||((!left)&&up)) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } if(maze[i+1][j]) { code[j]=0; code[j-1]=t; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j]=code[j-1]=13; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]); } code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]); } } } void init() { memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { maze[i][j]=1; scanf("%d",&score[i][j]); } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,0); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { hm[cur^1].init(); dpblank(i,j,cur); cur^=1; } int ans=0; for(int i=0;i<hm[cur].size;i++) ans+=hm[cur].dp[i]; printf("%d\n",ans); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int iCase=0; while(scanf("%d%d",&N,&M)!=EOF) { iCase++; printf("Case %d: ",iCase); init(); if(N==1&&M==1) { printf("%d\n",score[1][1]); continue; } solve(); } return 0; } /* Sample Input 2 2 1 2 3 1 3 3 0 -20 100 1 -20 -20 1 1 1 Sample Output Case 1: 5 Case 2: 61 */
POJ 1739 Tony's Tour 樓教主的男人八題之一。從左下角走到右下角,每個非障礙格子僅走一次的方法數。一種方法是在后面增加兩行轉換成回路問題,這樣就和ural 1519一樣了。也可以不增加行,直接特殊處理下即可。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/09/30/2709114.html

/* POJ 1739 題目意思就是從左下角走到右下角,每個非障礙格子都走一遍的方法數 轉換成回路問題。 在最后加兩行 .########. .......... 這樣就轉成回路問題了,就和URAL 1519 一樣的做法了 G++ 47ms */ #include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=1000010; int N,M; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD];//最小表示法使用 int ex,ey;//最后一個非障礙格子的坐標 struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE],f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,long long ans) { int i; int h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { f[i]+=ans; return; } state[size]=st; f[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st) { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } long long encode(int *code,int m) { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up) { if(i==ex&&j==ey)//只能出現在最后一個非障礙格子 { code[j]=code[j-1]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else //不在同一個連通分量則合並 { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==left) code[t]=up; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else if((left&&(!up))||((!left)&&up)) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j-1]=t; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } char str[20]; void init() { memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) { scanf("%s",&str); for(int j=0;j<M;j++) if(str[j]=='.') { maze[i][j+1]=1; } } maze[N+1][1]=maze[N+1][M]=1; for(int i=2;i<M;i++)maze[N+1][i]=0; for(int i=1;i<=M;i++)maze[N+2][i]=1; N+=2; ex=N,ey=M; } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,1); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } long long ans=0; for(int i=0;i<hm[cur].size;i++) ans+=hm[cur].f[i]; printf("%I64d\n",ans); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d%d",&N,&M)) { if(N==0&&M==0)break; init(); solve(); } return 0; }

/* POJ 1739 不增加行。 起點和終點特殊處理 G++ 47ms */ #include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=1000010; int N,M; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD];//最小表示法使用 int ex,ey;//最后一個非障礙格子的坐標 struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE],f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,long long ans) { int i; int h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { f[i]+=ans; return; } state[size]=st; f[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st) { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } long long encode(int *code,int m) { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if((i==N&&j==1)||(i==N&&j==M))//起點和終點 { if((left&&(!up))||((!left)&&up)) { code[j]=code[j-1]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } else if(left==0&&up==0) { if(maze[i][j+1]) { code[j-1]=0; code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j-1]=13; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } continue; } if(left&&up) { if(left==up) {//這種情況不能發生 /* if(i==ex&&j==ey)//只能出現在最后一個非障礙格子 { code[j]=code[j-1]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); }*/ } else //不在同一個連通分量則合並 { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==left) code[t]=up; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else if((left&&(!up))||((!left)&&up)) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j-1]=t; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } char str[20]; void init() { memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) { scanf("%s",&str); for(int j=0;j<M;j++) if(str[j]=='.') { maze[i][j+1]=1; } } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,1); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } long long ans=0; for(int i=0;i<hm[cur].size;i++) ans+=hm[cur].f[i]; printf("%I64d\n",ans); } int main() { //freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); while(scanf("%d%d",&N,&M)) { if(N==0&&M==0)break; init(); solve(); } return 0; }
POJ 3133 Manhattan Wiring 格子中有兩個2,兩個3.求把兩個2連起來,兩個3連起來。求經過總的格子數的總和減2. 兩條路徑不能交叉。有障礙格子。非障礙格子最多經過一次。插頭出需要記錄三種狀態:沒有插頭、2號插頭、3號插頭。可以用三進制。但是考慮到4進制更加高效,我用的四進制做的。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/09/30/2709263.html

/* POJ 3133 連接2的插頭為2,連接3的插頭為3 沒有插頭為0 用四進制表示(四進制比三進制高效) G++ 391ms */ #include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=1000010; int N,M; int maze[MAXD][MAXD];//0表示障礙,1是非障礙,2和3 int code[MAXD]; //0表示沒有插頭,2表示和插頭2相連,3表示和插頭3相連 struct HASHMAP { int head[HASH],next[STATE],size; int state[STATE]; int dp[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(int st,int ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { if(dp[i]>ans)dp[i]=ans; return; } state[size]=st; dp[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,int st)//四進制 { int i; for(int i=m;i>=0;i--) { code[i]=(st&3); st>>=2; } } int encode(int *code,int m) { int i; int st=0; for(int i=0;i<=m;i++) { st<<=2; st|=code[i]; } return st; } void shift(int *code,int m)//換行 { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up)//只能是相同的插頭 { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } } else if((left&&(!up))||((!left)&&up))//只有一個插頭 { int t; if(left)t=left; else t=up;//這里少寫個else ,查了好久的錯誤 if(maze[i][j+1]==1||maze[i][j+1]==t)//插頭從右邊出來 { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } if(maze[i+1][j]==1||maze[i+1][j]==t)//插頭從下邊出來 { code[j]=0; code[j-1]=t; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } } else if(left==0&&up==0)//沒有插頭 { code[j-1]=code[j]=0;//不加插頭 if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]); if(maze[i][j+1]&&maze[i+1][j]) { if(maze[i][j+1]==1&&maze[i+1][j]==1) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=2;//加2號插頭 hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); //decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=3;//加3號插頭 hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } else if((maze[i][j+1]==2&&maze[i+1][j]==1)||(maze[i+1][j]==2&&maze[i][j+1]==1)||(maze[i][j+1]==2&&maze[i+1][j]==2)) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=2; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } else if((maze[i][j+1]==3&&maze[i+1][j]==1)||(maze[i+1][j]==3&&maze[i][j+1]==1)||(maze[i][j+1]==3&&maze[i+1][j]==3)) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=3; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); if(code[j-1]!=0||code[j]!=0)continue; code[j-1]=code[j]=0;//不加插頭 if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]); } } void dp_2(int i,int j,int cur) { int left,up,k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if((left==2&&up==0)||(left==0&&up==2)) { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } else if(left==0&&up==0) { if(maze[i][j+1]==1||maze[i][j+1]==2) { code[j-1]=0; code[j]=2; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } if(maze[i+1][j]==1||maze[i+1][j]==2) { code[j-1]=2; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } } } } void dp_3(int i,int j,int cur) { int left,up,k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if((left==3&&up==0)||(left==0&&up==3)) { code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } else if(left==0&&up==0) { if(maze[i][j+1]==1||maze[i][j+1]==3) { code[j-1]=0; code[j]=3; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } if(maze[i+1][j]==1||maze[i+1][j]==3) { code[j-1]=3; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1); } } } } void init() { memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { scanf("%d",&maze[i][j]); //if(maze[i][j]==0)maze[i][j]=1; //if(maze[i][j]==1)maze[i][j]=0; //上面的寫法是錯的,!!! if(maze[i][j]==1||maze[i][j]==0)maze[i][j]^=1;//0變1,1變0 } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,0); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j]==0)dpblock(i,j,cur); else if(maze[i][j]==1)dpblank(i,j,cur); else if(maze[i][j]==2)dp_2(i,j,cur); else if(maze[i][j]==3)dp_3(i,j,cur); cur^=1; } int ans=0; for(int i=0;i<hm[cur].size;i++) ans+=hm[cur].dp[i]; if(ans>0)ans-=2; printf("%d\n",ans); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d%d",&N,&M)) { if(N==0&&M==0)break; init(); solve(); } return 0; }
ZOJ 3466 The Hive II 和HDU 1693一樣,求多回路問題。但是四邊形換成了六邊行。難度相應提高了。做的方法是一樣的。我是倒過來,按照列來轉移的,稍微簡單一下。按照行可以做,但是討論比較多,我沒有去嘗試。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/09/29/2708909.html

/* ZOJ 3466 C++ 3850ms 62768K 原來ZOJ中的long long要用%lld輸出啊 對ZOJ不熟悉啊,被坑了好久 */ #include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std; const int HASH=10007; const int STATE=2000010; const int MAXD=32; int N,M; int code[MAXD],maze[MAXD][MAXD]; struct HASHMAP { int head[HASH],next[STATE],state[STATE],size; long long f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(int st,long long ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(st==state[i]) { f[i]+=ans; return; } f[size]=ans; state[size]=st; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,int st) { int i; for(i=m;i>=0;i--) { code[i]=st&1; st>>=1; } } int encode(int *code,int m) { int i,st=0; for(i=0;i<=m;i++) { st<<=1; st|=code[i]; } return st; } void init() { N=8;//倒過來,8行 int t; scanf("%d",&t); memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) maze[i][j]=1; char str[10]; while(t--) { scanf("%s",&str); maze[str[1]-'A'+1][M-(str[0]-'A')]=0; } } void shift(int *code,int m)//偶數行換奇數行的時候要變化 { for(int i=m;i>1;i--) code[i]=code[i-2]; code[0]=0; code[1]=0; } void dpblank(int i,int j,int cur) { int k,left,up1,up2; int t1,t2; if(i%2==0)t1=j,t2=j+1; else t1=j-1,t2=j; for(k=0;k<hm[cur].size;k++) { decode(code,2*M,hm[cur].state[k]); left=code[2*(j-1)]; up1=code[2*j-1]; up2=code[2*j]; // printf("%d %d: %d %d %d\n",i,j,left,up1,up2); if((left==1&&up1==1&&up2==0)||(left==0&&up1==1&&up2==1)||(left==1&&up1==0&&up2==1)) { code[2*j-2]=code[2*j-1]=code[2*j]=0; if(j==M&&i%2==0)shift(code,2*M); hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } else if((left==1&&up1==0&&up2==0)||(left==0&&up1==1&&up2==0)||(left==0&&up1==0&&up2==1)) { if(maze[i+1][t1]) { code[2*j-2]=1; code[2*j-1]=code[2*j]=0; if(j==M&&i%2==0)shift(code,2*M); hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } if(maze[i+1][t2]) { code[2*j-2]=code[2*j]=0; code[2*j-1]=1; hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } if(maze[i][j+1]) { code[2*j-2]=code[2*j-1]=0; code[2*j]=1; hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } } else if(left==0&&up1==0&&up2==0) { if(maze[i+1][t1]&&maze[i+1][t2]) { code[2*j-2]=code[2*j-1]=1; code[2*j]=0; hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } if(maze[i+1][t1]&&maze[i][j+1]) { code[2*j-2]=code[2*j]=1; code[2*j-1]=0; hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } if(maze[i+1][t2]&&maze[i][j+1]) { code[2*j-2]=0; code[2*j-1]=code[2*j]=1; hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,2*M,hm[cur].state[k]); code[2*j-2]=code[2*j-1]=code[2*j]=0; if(j==M&&i%2==0)shift(code,2*M); hm[cur^1].push(encode(code,2*M),hm[cur].f[k]); } } void solve() { int i,j,cur=0; long long ans=0; hm[cur].init(); hm[cur].push(0,1); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } for(i=0;i<hm[cur].size;i++) if(hm[cur].state[i]==0) ans+=hm[cur].f[i]; printf("%lld\n",ans);//ZOJ中的C++要lld才能AC的 } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d",&M)!=EOF) { init(); solve(); } return 0; }
ZOJ 3256 Tour in the Castle 這題數據很大。求左上角到右下角的路徑數。經過所以格子一次。要用矩陣加速。由於每一列都是一樣的,狀態轉移就相同。用插頭DP求出列與列之間的狀態轉移,然后用矩陣乘法。這題比較難
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709831.html

/* ZOJ 3256 N*M(2<=N<=7,1<=M<=10^9)的方格,問從左上角的格子到左下角的格子, 而且僅經過所有格子一次的路徑數 插頭DP+矩陣加速 對於一個圖的鄰接矩陣的N次方,其中(i,j)位置上的元素表示 點i經過N步到達點j的方案數 */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; const int STATE=1010; const int HASH=419;//這個小一點,效率高 const int MOD=7777777; int N,M; int D; int code[10]; int ch[10]; int g[200][200];//狀態轉移圖 struct Matrix { int n,m; int mat[200][200]; }; Matrix mul(Matrix a,Matrix b)//矩陣相乘,要保證a的列數和b的行數相等 { Matrix ret; ret.n=a.n; ret.m=b.m; long long sum; for(int i=0;i<a.n;i++) for(int j=0;j<b.m;j++) { sum=0; for(int k=0;k<a.m;k++) { sum+=(long long)a.mat[i][k]*b.mat[k][j]; //sum%=MOD;//加了這句話就會TLE,坑啊。。。 } ret.mat[i][j]=sum%MOD; } return ret; } Matrix pow_M(Matrix a,int n)//方陣的n次方 { Matrix ret=a; memset(ret.mat,0,sizeof(ret.mat)); for(int i=0;i<a.n;i++)ret.mat[i][i]=1;//單位陣 Matrix temp=a; while(n) { if(n&1)ret=mul(ret,temp); temp=mul(temp,temp); n>>=1; } return ret; } struct HASHMAP { int head[HASH],next[STATE],size; int state[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } int push(int st) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) return i; state[size]=st; next[size]=head[h]; head[h]=size++; return size-1; } }hm; void decode(int *code,int n,int st) { for(int i=n-1;i>=0;i--) { code[i]=st&3; st>>=2; } } int encode(int *code,int n) { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; int st=0; for(int i=0;i<n;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=2; st|=code[i]; } return st; } bool check(int st,int nst)//判斷兩種狀態能不能轉移 { decode(code,N,st); int flag=0;//標記格子上邊是否有插頭 int cnt=0; int k; for(int i=0;i<N;i++) { if(flag==0)//這個格子上邊沒有插頭 { if(code[i]==0&&(nst&(1<<i))==0)//左邊和右邊都沒有插頭 return false; if(code[i]&&(nst&(1<<i)))continue; if(code[i])flag=code[i];//插頭從左邊過來,從下邊出去 else flag=-1;//插頭從下邊進來從右邊出去 k=i; } else { if(code[i]&&(nst&(1<<i)))//左邊和右邊和上邊都有插頭 return false; if(code[i]==0&&(nst&(1<<i))==0)continue; if(code[i]) { if(code[i]==flag&&((nst!=0)||i!=N-1))return false;//只有最后一個格子才能合起來 if(flag>0) { for(int j=0;j<N;j++) if(code[j]==code[i]&&j!=i) code[j]=code[k]; code[i]=code[k]=0; } else { code[k]=code[i]; code[i]=0; } } else { if(flag>0)code[i]=code[k],code[k]=0; else code[i]=code[k]=N+(cnt++); } flag=0; } } if(flag!=0)return false; return true; } struct Node { int g[200][200]; int D; }node[20];//打表之用 void init() { if(node[N].D!=0) { memcpy(g,node[N].g,sizeof(node[N].g)); D=node[N].D; return; } int st,nst; hm.init(); memset(code,0,sizeof(code)); code[0]=code[N-1]=1; hm.push(0); hm.push(encode(code,N)); memset(g,0,sizeof(g)); for(int i=1;i<hm.size;i++) { st=hm.state[i]; for(nst=0;nst<(1<<N);nst++) if(check(st,nst)) { int j=hm.push(encode(code,N)); g[i][j]=1; } } D=hm.size; memcpy(node[N].g,g,sizeof(g)); node[N].D=D; } void solve() { Matrix temp; temp.n=temp.m=D; memcpy(temp.mat,g,sizeof(g)); Matrix ans=pow_M(temp,M); if(ans.mat[1][0]==0)printf("Impossible\n"); else printf("%d\n",ans.mat[1][0]%MOD); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); for(int i=0;i<20;i++)node[i].D=0; while(scanf("%d%d",&N,&M)==2) { init(); solve(); } return 0; }
ZOJ 3213 Beautiful Meadow 此題求簡單路徑。得到最大的分數。
用最小表示法,需要增加標志位記錄獨立插頭個數。要使獨立插頭個數小於等於2.
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710055.html

/* ZOJ 3213 */ #include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std; const int MAXD=15; const int HASH=10007; const int STATE=1000010; int N,M; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD]; int num;//獨立插頭的個數 int ans;//答案 struct HASHMAP { int head[HASH],next[STATE],size; int state[STATE],dp[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(int st,int ans) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { if(dp[i]<ans)dp[i]=ans; return; } state[size]=st; dp[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,int st) { num=st&7;//獨立插頭個數 st>>=3; for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } int encode(int *code,int m) { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; int st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } st<<=3; st|=num; return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left!=up) { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==up) code[t]=left; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]); // hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]); } } else if(left||up) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]); } if(maze[i+1][j]) { code[j-1]=t; code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]); } if(num<2) { num++; code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]); } } else { code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]); if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]); } if(num<2) { num++; if(maze[i][j+1]) { code[j]=13; code[j-1]=0; hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]); } if(maze[i+1][j]) { code[j-1]=13; code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]); } } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]);//這個忘記了!!! code[j-1]=code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].dp[k]); } } void init() { scanf("%d%d",&N,&M); ans=0; memset(maze,0,sizeof(maze));//初始化別忘記了 for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { scanf("%d",&maze[i][j]); if(maze[i][j]>ans)ans=maze[i][j]; } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,0); for(i=1;i<=N;i++) for(int j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } for(i=0;i<hm[cur].size;i++) if(hm[cur].dp[i]>ans) ans=hm[cur].dp[i]; printf("%d\n",ans); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { init(); solve(); } return 0; } /* Sample Input 2 1 1 10 1 2 5 0 Sample Output 10 5 */
HDU 4285 circuits 這是天津網絡賽的題目。求K個回路的方案數。而且不能是環套環。
增加個標志位來記錄形成的回路個數。而且注意避免環套環的情況。
解題報告:
http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710308.html

/* HDU 4285 要形成剛好K條回路的方法數 要避免環套環的情況。 所以形成回路時,要保證兩邊的插頭數是偶數 G++ 11265ms 11820K C++ 10656ms 11764K */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; const int MAXD=15; const int STATE=1000010; const int HASH=300007;//這個大一點可以防止TLE,但是容易MLE const int MOD=1000000007; int N,M,K; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD]; int num;//圈的個數 struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE]; int f[STATE]; void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,int ans) { int i; int h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st) { f[i]+=ans; f[i]%=MOD; return; } state[size]=st; f[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st) { num=st&63; st>>=6; for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } long long encode(int *code,int m)//最小表示法 { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } st<<=6; st|=num; return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up) { if(num>=K)continue; int t=0; //要避免環套環的情況,需要兩邊插頭數為偶數 for(int p=0;p<j-1;p++) if(code[p])t++; if(t&1)continue; if(num<K) { num++; code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]); } } else { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==up) code[t]=left; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]); } } else if(left||up) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if(maze[i+1][j]) { code[j]=0; code[j-1]=t; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]); } } char str[20]; void init() { scanf("%d%d%d",&N,&M,&K); memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) { scanf("%s",&str); for(int j=1;j<=M;j++) if(str[j-1]=='.') maze[i][j]=1; } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,1); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } int ans=0; for(i=0;i<hm[cur].size;i++) if(hm[cur].state[i]==K) { ans+=hm[cur].f[i]; ans%=MOD; } printf("%d\n",ans); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { init(); solve(); } return 0; } /* Sample Input 2 4 4 1 **.. .... .... .... 4 4 1 .... .... .... .... Sample Output 2 6 */

/* HDU 4285 要形成剛好K條回路的方法數 要避免環套環的情況。 所以形成回路時,要保證兩邊的插頭數是偶數 G++ 11765ms 12560K C++ 11656ms 12504K */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; const int MAXD=15; const int STATE=1000010; const int HASH=100007; const int MOD=1000000007; int N,M,K; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD]; struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE]; int f[STATE]; int cir[STATE];//形成的圈的個數 void init() { size=0; memset(head,-1,sizeof(head)); } void push(long long st,int ans,int _cir) { int i,h=st%HASH; for(i=head[h];i!=-1;i=next[i]) if(state[i]==st&&cir[i]==_cir) { f[i]+=ans; f[i]%=MOD; return; } state[size]=st; f[size]=ans; cir[size]=_cir; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st) { for(int i=m;i>=0;i--) { code[i]=st&7; st>>=3; } } long long encode(int *code,int m)//最小表示法 { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m) { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur) { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up) { if(hm[cur].cir[k]>=K)continue; int t=0; for(int p=0;p<j-1;p++) if(code[p])t++; if(t&1)continue; if(hm[cur].cir[k]<K) { code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]+1); } } else { code[j-1]=code[j]=0; for(int t=0;t<=M;t++) if(code[t]==up) code[t]=left; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]); } } else if(left||up) { int t; if(left)t=left; else t=up; if(maze[i][j+1]) { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k],hm[cur].cir[k]); } if(maze[i+1][j]) { code[j]=0; code[j-1]=t; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]); } } else { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]); } } } } void dpblock(int i,int j,int cur) { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); code[j-1]=code[j]=0; hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]); } } char str[20]; void init() { scanf("%d%d%d",&N,&M,&K); memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) { scanf("%s",&str); for(int j=1;j<=M;j++) if(str[j-1]=='.') maze[i][j]=1; } } void solve() { int i,j,cur=0; hm[cur].init(); hm[cur].push(0,1,0); for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init(); if(maze[i][j])dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } int ans=0; for(i=0;i<hm[cur].size;i++) if(hm[cur].cir[i]==K) { ans+=hm[cur].f[i]; ans%=MOD; } printf("%d\n",ans); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { init(); solve(); } return 0; } /* Sample Input 2 4 4 1 **.. .... .... .... 4 4 1 .... .... .... .... Sample Output 2 6 */
上面各題的代碼,我都是用一樣的模板寫的,寫的風格都是一致的。插頭DP寫起來比較難。而且容易寫錯。更加復雜的插頭DP真的很難想到。
但是插頭DP還是很美妙的。更多插頭DP的題目等我做了會更新上去的。
持續更新中......
2012-10-2 by kuangbin