插头DP
本题为CDQ《基于连通性状态压缩的动态规划的……(我忘了)》里的例题!(嗯就是这样……)
先膜拜一下ccy大神……http://blog.sina.com.cn/s/blog_51cea4040100gmky.html
在这里将我当初看插头DP的一些不解之处写出来,给大家提供一些参考:
以前我老是搞不懂“左/右插头”的区分……今天终于搞明白了:左插头是一个联通块与轮廓线的左边的交点,右插头是靠右的交点……这下那些分情况讨论的状态转移瞬间就明白了= =
然后是状态表示……其实以前用状压都是一位表示一个状态,而插头DP由于用的是3(4)进制,所以是二进制下的两位来表示一个插头的状态(两位的话就可以表示0/1/2了,一位只能表示0/1)
状态转移其实就是删掉/加上插头的操作……看了代码很好懂……sigh……当年是一看长代码就头晕,唉
其实我们是在对每一个格子枚举可行状态的……要不然怎么叫统计方案数啊= =(这一点我一开始真的没想到……so sad……too naive)
其实在看插头DP代码的过程中也顺便理解了Hash……原来hash向后排是这个意思,开个数组记录下每个hash值所对应的原值就好了,好像也没那么难的样子……为什么当初我NOIP的时候就没学会呢……
1 //Ural 1519 2 #include<cmath> 3 #include<vector> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<iostream> 8 #include<algorithm> 9 #define rep(i,n) for(int i=0;i<n;++i) 10 #define F(i,j,n) for(int i=j;i<=n;++i) 11 #define D(i,j,n) for(int i=j;i>=n;--i) 12 #define CC(a,b) memset(a,b,sizeof(a)) 13 #define pb push_back 14 using namespace std; 15 int getint(){ 16 int v=0,sign=1; char ch=getchar(); 17 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 18 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 19 return v*=sign; 20 } 21 const int N=1e7+10; 22 typedef long long LL; 23 typedef unsigned long long u64; 24 /******************tamplate*********************/ 25 const int sz=199917; 26 bool mp[15][15]; 27 int n,m,nn,mm,k; 28 int tot[2],bit[15],hash[sz],state[2][sz]; 29 u64 dp[2][sz],ans; 30 void init(){ 31 CC(mp,0); 32 CC(dp,0); 33 tot[0]=dp[0][1]=1,ans=k=0; 34 state[0][1]=0; 35 char ch; 36 F(i,1,n){ 37 scanf("%c",&ch); 38 F(j,1,m){ 39 scanf("%c",&ch); 40 mp[i][j]=ch=='.'; 41 if (mp[i][j]) nn=i,mm=j; 42 } 43 } 44 } 45 void hash_in(int s,u64 sum){ 46 int p=s%sz; 47 while(hash[p]){ 48 if (state[k][hash[p]]==s){ 49 dp[k][hash[p]]+=sum; 50 return; 51 } 52 p++; 53 if (p==sz) p=0; 54 } 55 hash[p]=++tot[k]; 56 state[k][hash[p]]=s; 57 dp[k][hash[p]]=sum; 58 } 59 #define in hash_in(s,sum) 60 void work(){ 61 F(i,1,n){ 62 F(j,1,m){ 63 k^=1; 64 tot[k]=0; 65 CC(hash,0); 66 F(u,1,tot[1-k]){ 67 int s=state[1-k][u]; 68 u64 sum=dp[1-k][u]; 69 int p=(s>>bit[j-1])&3,q=(s>>bit[j])&3; 70 if (!mp[i][j]){ if(!p && !q) in; } 71 else{ 72 if (!p && !q){ 73 if (!mp[i][j+1]||!mp[i+1][j]) continue; 74 s=s^(1<<bit[j-1])^(1<<bit[j]<<1),in; 75 }else if(!p && q){ 76 if (mp[i][j+1]) in; 77 if (mp[i+1][j]) s=s^q*(1<<bit[j-1])^q*(1<<bit[j]),in; 78 }else if(p && !q){ 79 if (mp[i+1][j]) in; 80 if (mp[i][j+1]) s=s^p*(1<<bit[j-1])^p*(1<<bit[j]),in; 81 }else if(p+q==2){ 82 int nd=1; 83 F(u,j+1,m){ 84 int w=(s>>bit[u])&3; 85 if (w==1) nd++; 86 if (w==2) nd--; 87 if (!nd) {s-=(1<<bit[u]); break;}//这两个地方不能将+/-改成^ 88 //废话!因为这里是把右插头(2)改成左插头(1)了!用异或就把2改成3了……那算神马…… 89 } 90 s=s^(1<<bit[j])^(1<<bit[j-1]),in; 91 }else if(p+q==4){ 92 int nd=1; 93 D(u,j-2,1){ 94 int w=(s>>bit[u])&3; 95 if (w==2) nd++; 96 if (w==1) nd--; 97 if (!nd) {s+=(1<<bit[u]); break;}//这里是将左插头(1)改成右插头(2) 98 } 99 s=s^(1<<bit[j]<<1)^(1<<bit[j-1]<<1),in; 100 }else if(p==1 && q==2){ 101 if (i==nn && j==mm) ans+=sum; 102 }else if(p==2 && q==1) s=s^(1<<bit[j-1]<<1)^(1<<bit[j]),in; 103 } 104 } 105 } 106 F(j,1,tot[k]) state[k][j]<<=2; 107 } 108 printf("%llu ",ans); 109 } 110 111 int main(){ 112 #ifndef ONLINE_JUDGE 113 freopen("1519.in","r",stdin); 114 freopen("1519.out","w",stdout); 115 #endif 116 F(i,0,13) bit[i]=i<<1; 117 while(scanf("%d%d",&n,&m)!=EOF){ 118 init(); 119 work(); 120 } 121 return 0; 122 }