基于连通性的状压dp
巧妙之处:插头已经可以表示内部所有状态了。
就是讨论麻烦一些。
简介
转移方法:
逐格转移,分类讨论
记录状态方法:
最小表示法(每次要重新编号,对于一类没用“回路路径”之类的题,可以胜任)
括号表示法(便于操作,但是一些题不能记录状态)
状态存储方法:
不能直接循环所有可能状态,因为状态不满太浪费
哈希+滚动数组
(clear时候,直接memset(hd),cnt=0就是最快的!!!!)
然后具体题目分清楚转移情况讨论即可。
例题
尝试加入各种剪枝以减少状态量。
经典入门例题:HDU 1693
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=11; int n,m; int mp[N+2][N+2]; int T; ll f[N+2][N+2][1<<(N+1)]; ll ans; void wrk(){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ for(int s=0;s<(1<<m+1);s++){ if(f[i][j][s]==0) continue; if(j!=m){// not a end //cout<<" here "<<i<<" "<<j<<" "<<s<<endl; int le=(s>>j-1)&1; int up=(s>>j)&1; if(mp[i][j]==0){ if(le==0&&up==0){ f[i][j+1][s]+=f[i][j][s]; } } else{ int now=s; if(le==1&&up==0){ int s0=s^(1<<j-1); s0|=(1<<j); f[i][j+1][s0]+=f[i][j][s]; f[i][j+1][s]+=f[i][j][s]; } else if(le==1&&up==1){ int s0=s^(1<<j-1); s0^=(1<<j); f[i][j+1][s0]+=f[i][j][s]; } else if(le==0&&up==1){ int s0=s^(1<<j); s0|=(1<<j-1); f[i][j+1][s0]+=f[i][j][s]; f[i][j+1][s]+=f[i][j][s]; } else{ int s0=s|(1<<j); s0|=(1<<j-1); f[i][j+1][s0]+=f[i][j][s]; } } } else{// j==m int le=(s>>j-1)&1; int up=(s>>j)&1; if(mp[i][j]==0){ if(le==0&&up==0){ f[i+1][1][(s<<1)]+=f[i][j][s]; } } else{ int now=s; //cout<<" irhf "<<i<<" "<<j<<" : "<<s<<" "<<le<<" "<<up<<endl; if(le==1&&up==0){ f[i+1][1][s<<1]+=f[i][j][s]; } else if(le==1&&up==1){ int s0=s^(1<<j-1); s0^=(1<<j); f[i+1][1][s0<<1]+=f[i][j][s]; } else if(le==0&&up==1){ int s0=s^(1<<j); s0|=(1<<j-1); f[i+1][1][s0<<1]+=f[i][j][s]; } else{ continue; } } } } } } } int main() { scanf("%d",&T); for(int o=1;o<=T;o++){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&mp[i][j]); } } ans=0; memset(f,0,sizeof f); f[1][1][0]=1; wrk(); /*for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cout<<i<<" and "<<j<<" : "<<endl; for(int s=0;s<=(1<<m+1);s++){ cout<<" "<<s<<" |: "<<f[i][j][s]<<endl; } }*/ ans=f[n+1][1][0]; printf("Case %d: There are %lld ways to eat the trees. ",o,ans); } return 0; }
【模板】插头dp
https://www.luogu.org/problemnew/show/P5056
直接memset就是最好用的了。。。非要暴力清空结果TLE了一夜一页
对于下面和右面有障碍的,就不要填出插头了。剪枝
// luogu-judger-enable-o2 // luogu-judger-enable-o2 #include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=20; const int M=600000; const int mo=1<<19; int n,m; char mp[N][N]; int bit[N]; int lasx,lasy; int num(int s,int k){//s's kth pos return (s>>bit[k-1])&3; } void zip(int s){ for(reg i=1;i<=m+1;++i){ cout<<num(s,i)<<" "; }cout<<endl; } struct hah{ int val[M],hd[mo],nxt[M]; ll dp[M]; int cnt; void upda(int s,ll v){ int p=s%mo; // cout<<" upda ";zip(s); // cout<<"hash "<<s<<" v "<<v<<" p "<<p<<" cnt "<<cnt<<" hd "<<hd[p]<<endl; for(reg i=hd[p];i;i=nxt[i]){ // cout<<"i "<<i<<" nxt "<<nxt[i]<<endl; if(val[i]==s){ // cout<<" fin "<<i<<endl; dp[i]+=v;return; } } // cout<<"build new "<<endl; ++cnt;nxt[cnt]=hd[p];dp[cnt]=v;val[cnt]=s; hd[p]=cnt; } void clear(){ memset(hd,0,sizeof hd);cnt=0; } }f[2]; int ad(int k,int c){ // cout<<" k "<<k<<" "<<c<<" bit "<<bit[k-1]<<endl; return (1<<(bit[k-1]))*c; } int get(int s,int k){ int lp=num(s,k); if(lp==1){//after int cnt=0; for(reg i=k+1;i<=m+1;++i){ if(num(s,i)==1) ++cnt; else if(num(s,i)==2) --cnt; if(cnt<0) return i; } }else{//before int cnt=0; for(reg i=k-1;i>=1;--i){ if(num(s,i)==1) --cnt; else if(num(s,i)==2) ++cnt; if(cnt<0) return i; } } cout<<" bug "<<endl; return -233;//bug } ll wrk(){ int tmp=0; f[tmp].upda(0,1); ll lim=(1<<(2*(m+1)))-1; for(reg i=1;i<=n;++i){ // cout<<" i "<<i<<endl; for(reg j=1;j<=f[tmp].cnt;++j) { f[tmp].val[j]=(f[tmp].val[j]*4)&lim; // cout<<" num "<<j<<" : "<<" dp "<<f[tmp].dp[j]<<" zip ";zip(f[tmp].val[j]); } for(reg j=1;j<=m;++j){ // cout<<" j "<<j<<endl; tmp^=1; f[tmp].clear(); int pr=tmp^1; // cout<<" cnt "<<f[pr].cnt<<endl; for(reg k=1;k<=f[pr].cnt;++k){ // cout<<" k "<<k<<endl; int s=f[pr].val[k]; // int u=num(s,j+1),l;//=num(s,j); ll v=f[pr].dp[k]; // cout<<" v "<<v;cout<<" s ";zip(s);//endl; if(mp[i][j]=='*'){ if(num(s,j)==0&&num(s,j+1)==0) f[tmp].upda(s,v); }else{ if(num(s,j)==0&&num(s,j+1)==0){//new if(j!=m)f[tmp].upda(s+ad(j,1)+ad(j+1,2),v); } else if(num(s,j)==0){//only up // cout<<" only up"<<endl; f[tmp].upda(s+ad(j,num(s,j+1))+ad(j+1,-num(s,j+1)),v); if(j!=m)f[tmp].upda(s,v); }else if(num(s,j+1)==0){//only left // cout<<" only left "<<endl; if(j!=m)f[tmp].upda(s+ad(j,-num(s,j))+ad(j+1,num(s,j)),v); f[tmp].upda(s,v); // cout<<" after upda "<<endl; }else{//both // cout<<" both "<<endl; if(num(s,j)==1&&num(s,j+1)==1){ int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1)); to+=ad(get(s,j+1),-1); f[tmp].upda(to,v); }else if(num(s,j)==2&&num(s,j+1)==2){ int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1)); to+=ad(get(s,j),1); f[tmp].upda(to,v); }else if(num(s,j)==2&&num(s,j+1)==1){ int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1)); f[tmp].upda(to,v); }else{ if(i==lasx&&j==lasy){ int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1)); f[tmp].upda(to,v); } } } } } } } for(reg i=1;i<=f[tmp].cnt;++i){ if(f[tmp].val[i]==0) return f[tmp].dp[i]; } return 0; } int main(){ rd(n);rd(m); for(reg i=1;i<=19;++i) bit[i]=i<<1; for(reg i=1;i<=n;++i){ scanf("%s",mp[i]+1); } for(reg i=1;i<=n;++i){ for(reg j=1;j<=m;++j){ if(mp[i][j]=='.') lasx=i,lasy=j; } } cout<<wrk(); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/3/31 21:44:47 */