1. 程式人生 > >[POJ 1739] Tony's Tour

[POJ 1739] Tony's Tour

con com tps HA .org memset flag 錯誤 除了

Link:

POJ 1739 傳送門

Solution:

這題除了一開始的預處理,基本上就是插頭$dp$的模板題了

由於插頭$dp$求的是$Hamilton$回路,而此題有起點和終點的限制

於是可以構造一條$[n,1]->[n+2,1]->[n+2,m]->[n,m]$的路徑,正好只添加一條$S->T$的路徑

接下來就是插頭$dp$的模板了

推薦三篇文章,看完基本上就懂插頭$dp$了吧,

litble:https://blog.csdn.net/litble/article/details/79369147

yhzq:遠航之曲博客

陳丹琦論文:http://www.doc88.com/p-9009338580746.html

可以發現,插頭$dp$其實就是對於當前已枚舉部分和未枚舉部分的輪廓線的狀壓$dp$

註意每枚舉過一行要將所有狀態左移一位(下一行會多出來一個狀態位)

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <utility>
#include <vector>

using namespace std;
typedef long long ll;

const int INF=0x3f3f3f3f,MAXN=15,MAX_State=3e5+10
,MAX_Hash=3e5; int n,m,pre,cur,dat[MAXN][MAXN]; int st[2][MAX_State],st_cnt[2],bit[20]; ll res[2][MAX_State],sum=0; struct edge{int to,next;}e[MAX_State]; int hs[MAX_State],hs_cnt=0; void ins(int now,ll x) { int p=now%MAX_Hash; for(int i=hs[p];i;i=e[i].next) //Hash時最好使用鏈式前向星 if(st[cur][e[i].to]==now){res[cur][e[i].to]+=x;return
;} st_cnt[cur]++; e[++hs_cnt].to=st_cnt[cur]; e[hs_cnt].next=hs[p]; hs[p]=hs_cnt; st[cur][st_cnt[cur]]=now;res[cur][st_cnt[cur]]=x; } void plugDP() { sum=st[cur][1]=cur=0; //註意初始化的順序 st_cnt[cur]=res[cur][1]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=st_cnt[cur];j++) //左移一位 st[cur][j]<<=2; for(int j=1;j<=m;j++) { hs_cnt=0;memset(hs,0,sizeof(hs)); pre=cur;cur^=1;st_cnt[cur]=0; for(int k=1;k<=st_cnt[pre];k++) { int now=st[pre][k];ll x=res[pre][k]; ll dw=(now>>bit[j-1])&3,rt=(now>>bit[j])&3; ll numd=1<<bit[j-1],numr=1<<bit[j]; if(!dat[i][j] && !dw && !rt) ins(now,x); else if(!dw && !rt && dat[i+1][j] && dat[i][j+1]) ins(now+numd+2*numr,x); else if(!dw && rt) { if(dat[i][j+1]) ins(now,x); if(dat[i+1][j]) ins(now-rt*numr+rt*numd,x); } else if(dw && !rt) { if(dat[i+1][j]) ins(now,x); if(dat[i][j+1]) ins(now-dw*numd+dw*numr,x); } else if(dw==1 && rt==1) { int flag=1; for(int l=j+1;l<=m;l++) { if(((now>>bit[l])&3)==1) flag++; if(((now>>bit[l])&3)==2) flag--; if(!flag){ins(now-numd-numr-(1<<bit[l]),x);break;} } } else if(dw==2 && rt==2) { int flag=-1; for(int l=j-2;l>=0;l--) { if(((now>>bit[l])&3)==1) flag++; if(((now>>bit[l])&3)==2) flag--; if(!flag){ins(now-2*numd-2*numr+(1<<bit[l]),x);break;} } } else if(dw==2 && rt==1) ins(now-2*numd-numr,x); else if(dw==1 && rt==2 && i==n && j==m) sum+=x; } } } } int main() { for (int i=1;i<15;i++) bit[i]=i<<1; while(~scanf("%d%d",&n,&m) && n && m) { memset(dat,0,sizeof(dat)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { char ch=getchar(); while(ch!=. && ch!=#) ch=getchar(); if(ch==.) dat[i][j]=1; } n+=2;dat[n-1][1]=dat[n-1][m]=1; //預處理 for(int i=1;i<=m;i++) dat[n][i]=1; plugDP();printf("%lld\n",sum); } return 0; }

Review:

做的時候犯的絲帛錯誤:

1、對變量初始化的先後順序要註意!!!

EX:$cur=0$要在$st[cur][1]$之前初始化

2、哈希表最好用鏈式前向星實現

用$vector$時TLE了,可能是$vector.clear()$的時間太長了?

鏈式前向星的一大優點就是重復使用時不用清空數組,只要$tot=0$即可

3、左移、右移比$==$優先級高,但位與、位或比$==$優先級低!!

[POJ 1739] Tony's Tour