這個題的搜索能夠打到48分……ide
#include <cstdio> #include <cstring> #include <algorithm> const int N=12; bool must[N],in[N]; int cnt; int n,a[N][N],q[N],b[N]; inline bool judge(int len,int lim){ return lim-len>=cnt; } inline bool check(int len){ register int i,j,k,pos,g; register bool can; for(i=1;i<=n;++i){ can=false; for(j=1;j<=len;++j){ if(len-j+1>=a[i][0]){ pos=0,g=0; for(k=j;k<=len;++k){ if(in[q[k]])continue; if(q[k]!=a[i][pos+1]){g=-1;break;} b[++pos]=q[k]; in[q[k]]=true; } can=g!=-1&&pos==a[i][0]; while(pos--)in[b[pos+1]]=false; if(can)break; } if(j>=a[i][0]){ pos=0,g=0; for(k=j;k>0;--k){ if(in[q[k]])continue; if(q[k]!=a[i][pos+1]){g=-1;break;} b[++pos]=q[k]; in[q[k]]=true; } can=g!=-1&&pos==a[i][0]; while(pos--)in[b[pos+1]]=false; if(can)break; } } if(!can)return false; } return true; } inline bool dfs(int pos,int lim){ if(!judge(pos-1,lim))return false; if(pos==lim+1)return check(pos-1); register int i; register bool keep; for(i=1;i<=9;++i) if(i!=q[pos-1]){ q[pos]=i; if(must[i]){ must[i]=false,--cnt; keep=true; }else keep=false; if(dfs(pos+1,lim))return true; if(keep)must[i]=true,++cnt; } return false; } int main(){ //freopen("rio.in","r",stdin); scanf("%d",&n); register int i,j; for(i=1;i<=n;++i){ while(true){ scanf("%d",&j); if(!j)break; a[i][++a[i][0]]=j; must[j]=true; } } for(i=1;i<=9;++i) cnt+=must[i]; for(i=cnt;i<=10;++i){ if(cnt<5&&i==10)break; if(dfs(1,i)){ printf("%d\n",i); return 0; } } puts("-1"); return 0; }
搜索的時候減枝實在是過重要了,其次夢想與搜索之間的平衡也是很重要的,可是必定要合理且適當.
正解很神.
貼一發題解:spa
還有一位大佬的代碼註釋也很好:https://loj.ac/submission/66283
我就是看的這兩個資料,而後碼出來的.(把寫法從spfa改爲記憶化dfs會快3倍……)
在這裏說一下坑吧,就是正着走的狀態能夠在半截出現,倒着走的狀態能夠在半截消失.
分享一下代碼.
正着走的單向40分:設計
#include <cstdio> #include <cstring> #include <algorithm> const int N=12; const int F=2010; const int Inf=0x3f3f3f3f; int n,ans,full,bit[N],a[N][N],l[N],b[N]; int f[N][N][F]; bool ex[N][N],can[N][N][N],in[N],fin[N][N][F]; inline bool check(int id,int len,int to){ register int i,pos=0; memset(in,0,sizeof(in)); for(i=1;i<=len;++i) in[a[id][i]]=true; for(i=1;i<=l[to];++i){ if(in[a[to][i]])continue; b[++pos]=a[to][i]; in[a[to][i]]=true; } if(pos!=l[id]-len)return false; for(i=len+1;i<=l[id];++i) if(a[id][i]!=b[i-len]) return false; return true; } inline void dfs(int id,int pos,int st){ //printf("%d %d %d\n",id,pos,st); if(fin[id][pos][st]){ //puts("GG"); ans=std::min(ans,f[id][pos][st]); return; } register int i; for(i=1;i<=n;++i) if(i!=id&&(bit[i]&st)==0) if(can[id][pos][i]&&f[i][0][st|bit[id]]>f[id][pos][st]){ f[i][0][st|bit[id]]=f[id][pos][st]; dfs(i,0,st|bit[id]); } if(pos!=l[id]&&f[id][pos+1][st]>f[id][pos][st]+1){ f[id][pos+1][st]=f[id][pos][st]+1; dfs(id,pos+1,st); } } int main(){ //freopen("rio.in","r",stdin); scanf("%d",&n); full=(1<<n)-1; register int i,j,k; for(i=1;i<=n;++i){ bit[i]=1<<(i-1); while(true){ scanf("%d",&j); if(!j)break; a[i][++l[i]]=j; ex[i][j]=true; } } for(i=1;i<=n;++i) for(j=0;j<=l[i];++j) for(k=1;k<=n;++k) can[i][j][k]=check(i,j,k); memset(f,0x3f,sizeof(f)); ans=Inf; for(i=1;i<=n;++i) fin[i][l[i]][full^bit[i]]=true; for(i=1;i<=n;++i){ f[i][0][0]=0; dfs(i,0,0); } printf("%d\n",ans==Inf?-1:ans); return 0; }
倒着走的單向40分:3d
#include <cstdio> #include <cstring> #include <algorithm> const int N=12; const int F=2010; const int Inf=0x3f3f3f3f; int n,ans,full,bit[N],a[N][N],l[N],b[N]; int f[N][N][F]; bool can[N][N][N],in[N],fin[N][N][F]; inline bool check(int id,int len,int to){ register int i,pos=0; memset(in,0,sizeof(in)); for(i=1;i<=len;++i) in[a[id][i]]=true; for(i=1;i<=l[to];++i){ if(in[a[to][i]])continue; b[++pos]=a[to][i]; in[a[to][i]]=true; } if(pos!=l[id]-len)return false; for(i=len+1;i<=l[id];++i) if(a[id][i]!=b[i-len]) return false; return true; } inline void dfs(int id,int pos,int st){ //printf("%d %d %d\n",id,pos,st); if(fin[id][pos][st]){ //puts("GG"); ans=std::min(ans,f[id][pos][st]); return; } if(!pos){ register int i,j; for(i=1;i<=n;++i) if(i!=id&&(bit[i]&st)==0) for(j=0;j<=l[i];++j) if(can[i][j][id]&&f[i][j][st|bit[id]]>f[id][pos][st]){ f[i][j][st|bit[id]]=f[id][pos][st]; dfs(i,j,st|bit[id]); } } if(pos&&f[id][pos-1][st]>f[id][pos][st]+1){ f[id][pos-1][st]=f[id][pos][st]+1; dfs(id,pos-1,st); } } int main(){ //freopen("rio.in","r",stdin); scanf("%d",&n); full=(1<<n)-1; register int i,j,k; for(i=1;i<=n;++i){ bit[i]=1<<(i-1); while(true){ scanf("%d",&j); if(!j)break; a[i][++l[i]]=j; } } for(i=1;i<=n;++i) for(j=0;j<=l[i];++j) for(k=1;k<=n;++k) can[i][j][k]=check(i,j,k); memset(f,0x3f,sizeof(f)); ans=Inf; for(i=1;i<=n;++i) fin[i][0][full^bit[i]]=true; for(i=1;i<=n;++i){ f[i][l[i]][0]=0; dfs(i,l[i],0); } printf("%d\n",ans==Inf?-1:ans); return 0; }
通過我不斷修改的清真美麗100分代碼:code
#include <set> #include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; std::set<LL> ext; const int N=11; const int F=1050; const int Inf=0x3f3f3f3f; int n,full,bit[N],a[N][N],l[N]; int f[N][N][N][N][F]; bool ex[N][N][N],can[N][N][N],in[N],vis[N][N][N][N][F]; inline bool check(int id,int len,int to){ int i,pos=0; memcpy(in,ex[id][len],sizeof(in)); for(i=1;i<=l[to];++i){ if(in[a[to][i]])continue; ++pos,in[a[to][i]]=true; if(a[to][i]!=a[id][len+pos]) return false; } if(pos!=l[id]-len)return false; return true; } inline void Init(){ scanf("%d",&n); int i,j,k; LL key; for(i=1;i<=n;++i){ l[i]=0,key=0; memset(ex[i],0,sizeof(ex[i])); bit[i]=1<<(i-1),j=1; while(j){ scanf("%d",&j); key=key*10+j; a[i][++l[i]]=j; for(k=1;k<=l[i];++k) if(a[i][k]) ex[i][l[i]][a[i][k]]=true; } --l[i]; if(ext.count(key))--i,--n; ext.insert(key); } full=(1<<n)-1; for(i=1;i<=n;++i){ can[0][0][i]=true; for(j=0;j<=l[i];++j) for(k=1;k<=n;++k) can[i][j][k]=check(i,j,k); } memset(f,0x3f,sizeof(f)); } #define cover(id1,pos1,id2,pos2,st,add) (k=std::min(dfs(id1,pos1,id2,pos2,st)+add,k)) inline int dfs(int id1,int pos1,int id2,int pos2,int st){ if(st==full&&pos1==l[id1]&&!pos2)return 0; int i,j,&k=f[id1][pos1][id2][pos2][st]; if(vis[id1][pos1][id2][pos2][st])return k; vis[id1][pos1][id2][pos2][st]=true; if(id2&&!pos2){ for(i=0;i<=n;++i) if(i!=id1&&!(bit[i]&st)) for(j=0;j<=l[i];++j) if(can[i][j][id2]) cover(id1,pos1,i,j,st|bit[i],0); } for(i=1;i<=n;++i) if(can[id1][pos1][i]&&!(bit[i]&st)) cover(i,0,id2,pos2,st|bit[i],0); if(pos1==l[id1]&&!pos2)return k; if(pos1!=l[id1]&&(ex[id2][pos2+1][a[id1][pos1+1]]||!id2)) cover(id1,pos1+1,id2,pos2,st,1); if(pos2&&(ex[id1][pos1][a[id2][pos2]]||!id1)) cover(id1,pos1,id2,pos2-1,st,1); if(pos1!=l[id1]&&pos2&&a[id2][pos2]==a[id1][pos1+1]) cover(id1,pos1+1,id2,pos2-1,st,1); return k; } inline void Work(){ int i,ans=Inf; for(i=1;i<=n;++i) ans=std::min(dfs(0,0,i,l[i],bit[i]),ans); printf("%d\n",ans==Inf?-1:ans); } int main(){ Init(),Work(); return 0; }
我第一次A掉的時候寫的100分(這個代碼又臭又長又屎):blog
#include <cstdio> #include <cstring> #include <algorithm> const int N=12; const int F=1112; const int Inf=0x3f3f3f3f; int n,ans,full,bit[N],a[N][N],l[N],b[N]; int f[N][N][N][N][F]; bool ex[N][N][N],can[N][N][N],in[N],fin[N][N][N][N][F]; inline bool check(int id,int len,int to){ register int i,pos=0; memset(in,0,sizeof(in)); for(i=1;i<=len;++i) in[a[id][i]]=true; for(i=1;i<=l[to];++i){ if(in[a[to][i]])continue; b[++pos]=a[to][i]; in[a[to][i]]=true; } if(pos!=l[id]-len)return false; for(i=len+1;i<=l[id];++i) if(a[id][i]!=b[i-len]) return false; return true; } inline void dfs(int id1,int pos1,int id2,int pos2,int st){ //printf("%d %d %d %d %d\n",id1,pos1,id2,pos2,st); if(fin[id1][pos1][id2][pos2][st]){ //puts("GG"); ans=std::min(ans,f[id1][pos1][id2][pos2][st]); return; } if(id2&&!pos2){ register int i,j; for(i=1;i<=n;++i) if(i!=id1&&i!=id2&&(bit[i]&st)==0) for(j=0;j<=l[i];++j) if(can[i][j][id2]&&f[id1][pos1][i][j][st|bit[id2]]>f[id1][pos1][id2][pos2][st]){ f[id1][pos1][i][j][st|bit[id2]]=f[id1][pos1][id2][pos2][st]; dfs(id1,pos1,i,j,st|bit[id2]); } if(id1){ if(f[id1][pos1][0][0][st|bit[id2]]>f[id1][pos1][id2][pos2][st]){ f[id1][pos1][0][0][st|bit[id2]]=f[id1][pos1][id2][pos2][st]; dfs(id1,pos1,0,0,st|bit[id2]); } }else{ for(i=1;i<=n;++i) if(i!=id2&&(bit[i]&st)==0&&f[i][0][0][0][st|bit[id2]]>f[id1][pos1][id2][pos2][st]){ f[i][0][0][0][st|bit[id2]]=f[id1][pos1][id2][pos2][st]; dfs(i,0,0,0,st|bit[id2]); } } } if(id1){ register int i; for(i=1;i<=n;++i) if(i!=id1&&i!=id2&&(bit[i]&st)==0) if(can[id1][pos1][i]&&f[i][0][id2][pos2][st|bit[id1]]>f[id1][pos1][id2][pos2][st]){ f[i][0][id2][pos2][st|bit[id1]]=f[id1][pos1][id2][pos2][st]; dfs(i,0,id2,pos2,st|bit[id1]); } }else{ register int i; for(i=1;i<=n;++i) if(i!=id2&&(bit[i]&st)==0&&f[i][0][id2][pos2][st]>f[id1][pos1][id2][pos2][st]){ f[i][0][id2][pos2][st]=f[id1][pos1][id2][pos2][st]; dfs(i,0,id2,pos2,st); } } if((id2==0||pos2==0)&&(id1==0||pos1==l[id1]))return; if(id1&&pos1!=l[id1]&&(ex[id2][pos2+1][a[id1][pos1+1]]||!id2)&&f[id1][pos1+1][id2][pos2][st]>f[id1][pos1][id2][pos2][st]+1){ f[id1][pos1+1][id2][pos2][st]=f[id1][pos1][id2][pos2][st]+1; dfs(id1,pos1+1,id2,pos2,st); } if(id2&&pos2&&(ex[id1][pos1+1][a[id2][pos2]]||!id1)&&f[id1][pos1][id2][pos2-1][st]>f[id1][pos1][id2][pos2][st]+1){ f[id1][pos1][id2][pos2-1][st]=f[id1][pos1][id2][pos2][st]+1; dfs(id1,pos1,id2,pos2-1,st); } //printf("<>%d %d %d %d %d\n",id1,pos1,id2,pos2,st); if(id1&&id2&&pos2&&pos1!=l[id1]&&a[id2][pos2]==a[id1][pos1+1]&&f[id1][pos1+1][id2][pos2-1][st]>f[id1][pos1][id2][pos2][st]+1){ //puts("OK"); f[id1][pos1+1][id2][pos2-1][st]=f[id1][pos1][id2][pos2][st]+1; dfs(id1,pos1+1,id2,pos2-1,st); } //else puts("NO"); } int main(){ //freopen("rio.in","r",stdin); //freopen("wq.out","w",stdout); scanf("%d",&n),full=(1<<n)-1; register int i,j,k; for(i=1;i<=n;++i){ bit[i]=1<<(i-1); while(true){ scanf("%d",&j); if(!j)break; a[i][++l[i]]=j; } } for(i=1;i<=n;++i) for(j=0;j<=l[i];++j) for(k=1;k<=n;++k) can[i][j][k]=check(i,j,k); for(i=1;i<=n;++i) for(j=0;j<=11;++j) for(k=1;k<=j;++k) ex[i][j][a[i][k]]=true; memset(f,0x3f,sizeof(f)); ans=Inf; for(i=1;i<=n;++i){ for(j=1;j<=n;++j){ if(i==j)continue; fin[i][l[i]][j][0][full^bit[i]^bit[j]]=true; f[i][0][j][l[j]][0]=0; } fin[i][l[i]][0][0][full^bit[i]]=true; f[i][0][0][0][0]=0; fin[0][0][i][0][full^bit[i]]=true; f[0][0][i][l[i]][0]=0; } for(i=1;i<=n;++i){ for(j=1;j<=n;++j){ if(i==j)continue; dfs(i,0,j,l[j],0); } dfs(i,0,0,0,0); dfs(0,0,i,l[i],0); } printf("%d\n",ans==Inf?-1:ans); return 0; }
我在這裏說一下我對於這道題的解法的理解吧.
看到題解以後,就會發現這道題dp的狀態定義很謎.咱們先看正着走單向的,咱們之因此這麼定義,是由於最終方案也必定是這樣構成的,這很好理解.再看倒着走的,一樣,咱們之因此這麼定義,也是由於最終方案也必定是這樣構成的.那麼咱們再看雙向的狀態,思考一下,發現也是這樣的.那麼咱們這麼轉移就必定沒有問題.而這神奇的狀態設計從哪裏來呢.我以爲,就是抓出題目給出的操做的性質——「遠觀不如近看」,讓「近看」去知足「遠觀」,用「遠觀」去匹配「近看」,再加上增量構造的思路,就迸發出了這道題的狀態以及轉移.
如今以個人水平,我對於這道題的理解大概也就這麼多,也許之後會有更好的理解把.利用狀態壓縮,以及對於特殊的問題,設計合理的狀態,大概就是我從這道題裏學到的寶貴的東西,對於設計狀態,我仍是摸不着頭腦的.get