插头dp
我也来写一写总结,虽然我只做了那道例题和第一题(还没改过),
$Eat The Trees$是一道板子题,但是代码实现还是有点东西的,个人感受。。
丢一波代码:
#include<iostream> #include<cstring> #include<string> #include<cstdio> #define int long long #define Maxn 150 #define Sum ((1<<(m+1))-1) #define Reg register using namespace std; int t,n,m,pos,ans,A[Maxn][Maxn],f[3][1<<15]; signed main() { scanf("%lld",&t); for(Reg int o=1;o<=t;++o) { ans=pos=0; int sum=0; scanf("%lld%lld",&n,&m); for(Reg int i=1;i<=n;++i) { for(Reg int j=1;j<=m;++j) { scanf("%lld",&A[i][j]); if(A[i][j]) ++sum; } } if(sum%2!=0) {printf("Case %lld: There are 0 ways to eat the trees.\n",o); continue;} for(Reg int i=0;i<=Sum;++i) f[0][i]=f[1][i]=0; f[0][0]=1; for(Reg int i=1;i<=n;++i) { for(Reg int j=1,cur,pre;j<=m;++j) { ++pos; cur=pos&1,pre=(pos+1)&1; //清空 for(Reg int state=0;state<=Sum;++state) f[cur][state]=0; //更新 for(Reg int state=0;state<=Sum;++state) { if(j==1) //是第一个格点 { //如果最高位是1,不合条件,跳过 if((state&(1<<m))!=0) continue; //如果是障碍 if(A[i][j]==0) {if((state&1)==0) f[cur][state<<1]+=f[pre][state];} //不是障碍 else { //有上插头 只能接1个 if((state&1)!=0) { f[cur][((state^1)<<1)|1]+=f[pre][state]; f[cur][state<<1]+=f[pre][state]; } //没有上插头 只能接2个 else f[cur][(state<<1)|3]+=f[pre][state]; } } else if(j==m) //是最后一个格点 { //如果是障碍 if(A[i][j]==0) {if((state&(1<<m))==0&&(state&(1<<(m-1)))==0) f[cur][state]+=f[pre][state];} //不是障碍 else { //有左插头和上插头 更新答案或直接转移 if((state&(1<<(m-1)))!=0&&(state&(1<<m))!=0) f[cur][(state^(1<<j))^(1<<(j-1))]+=f[pre][state]; //只有上插头 只能向下 把最高位置成0 else if((state&(1<<(m-1)))==0&&(state&(1<<m))!=0) f[cur][(state^(1<<m))|(1<<(m-1))]+=f[pre][state]; //只有左插头 也是只能向下 else if((state&(1<<(m-1)))!=0&&(state&(1<<m))==0) f[cur][state]+=f[pre][state]; //都没有 不合法 跳过 else continue; } } else //是中间格点 { //如果是障碍 if(A[i][j]==0) {if((state&(1<<j))==0&&(state&(1<<(j-1)))==0) f[cur][state]+=f[pre][state];} //不是障碍 else { //如果只有上插头 可以往下接 往右接 if((state&(1<<j))!=0&&(state&(1<<(j-1)))==0) { //往下接 在j位置改为1 在j+1位置改为0 f[cur][(state|(1<<(j-1)))^(1<<j)]+=f[pre][state]; //往右接 不更改j位置和j+1位置(本来就是0或1) f[cur][state]+=f[pre][state]; } //如果只有左接头 仍然 else if((state&(1<<j))==0&&(state&(1<<(j-1)))!=0) { //往下接 不更改 f[cur][state]+=f[pre][state]; //往右接 在j位置改为0 在j+1位置改为1 f[cur][(state|(1<<j))^(1<<(j-1))]+=f[pre][state]; } //如果都有 不增加新插头 直接转移 else if((state&(1<<j))!=0&&(state&(1<<(j-1)))!=0) f[cur][(state^(1<<j))^(1<<(j-1))]+=f[pre][state]; //如果都没有 接右和下 else f[cur][(state|(1<<j))|(1<<(j-1))]+=f[pre][state]; } } } } } printf("Case %lld: There are %lld ways to eat the trees.\n",o,f[pos&1][0]); } return 0; }
然后就是第一道Ural 1519 Formula 1,
题目大意就是求所有的哈密顿回路的个数。
跟上一道题差不多,然后要新加一个3插头。
具体看代码($TLE$),没过
#include<iostream> #include<cstring> #include<string> #include<cstdio> #define Maxn 30 #define Sum ((1<<((m+1)*2))-1) #define Reg register using namespace std; bool vis[2][44739243]; int t,n,m,pos,lasx=0,lasy=0,ok=0,A[Maxn][Maxn],stack[2][4782969]; long long ans,f[2][44739243]; string s; inline int get(int state,int pos) {return ((state>>((pos-1)*2))&3);} //查询 inline int change(int state,int pos,int num) {return (((3<<((pos-1)*2)^(state|(3<<((pos-1)*2))))|(num<<((pos-1)*2))));} //更改 inline int update1(int state,int num,int pos) //寻找左括号并更改为右括号 { if(get(state,pos)==1) --num; //左括号 else if(get(state,pos)==2) ++num; //右括号 if(num==0) return change(state,pos,2); else return update1(state,num,pos-1); } inline int update2(int state,int num,int pos) //寻找右括号并更改为左括号 { if(get(state,pos)==1) ++num; //左括号 else if(get(state,pos)==2) --num; //右括号 if(num==0) return change(state,pos,1); else return update2(state,num,pos+1); } inline void insert(int cur,int num) { if(f[cur][num]==0) return; if(!vis[cur][num]) { vis[cur][num]=1; stack[cur][++stack[cur][0]]=num; } return; } signed main() { // freopen("text.in","r",stdin); scanf("%d%d",&n,&m); for(Reg int i=1;i<=n;++i) { cin>>s; for(Reg int j=1;j<=m;++j) { if(s[j-1]=='*') A[i][j]=0; else { A[i][j]=1; lasx=i,lasy=j; } } } f[0][0]=1,vis[0][0]=1,stack[0][++stack[0][0]]=0; for(Reg int i=1;i<=n;++i) { for(Reg int j=1,cur,pre;j<=m;++j) { ++pos; cur=pos&1,pre=(pos+1)&1; for(Reg int k=1;k<=stack[cur][0];++k) f[cur][stack[cur][k]]=vis[cur][stack[cur][k]]=0; //清空 stack[cur][0]=0; if(ok) break; for(Reg int k=1,state;k<=stack[pre][0];++k) //转移 { state=stack[pre][k]; if(j==1) //第一个格子 { if(get(state,m+1)!=0) continue; int plug=get(state,1); if(A[i][j]==0) { if(plug==0) { f[cur][state<<2]+=f[pre][state]; insert(cur,state<<2); } } //障碍 不能有插头 else //不是障碍 { if(plug==1) //1插头 { f[cur][state<<2]+=f[pre][state]; //新建向右的1插头 f[cur][change(change(state,1,0)<<2,1,1)]+=f[pre][state]; //新建向下的1插头 insert(cur,state<<2); insert(cur,change(change(state,1,0)<<2,1,1)); } else if(plug==2) //2插头 { f[cur][state<<2]+=f[pre][state]; //新建向右的2插头 f[cur][change(change(state,1,0)<<2,1,2)]+=f[pre][state]; //新建向下的2插头 insert(cur,state<<2); insert(cur,change(change(state,1,0)<<2,1,2)); } else if(plug==0) //无插头 要新建2个插头 一个1插头一个2插头 { f[cur][change(change(state<<2,1,1),2,2)]+=f[pre][state]; //新建向右的2插头和向下的1插头 insert(cur,change(change(state<<2,1,1),2,2)); } } } else if(j==m) //最后一个格子 { int plug1=get(state,m),plug2=get(state,m+1); if(A[i][j]==0) //障碍 { if(plug1==0&&plug2==0) { f[cur][state]+=f[pre][state]; insert(cur,state); } } else //不是障碍 { if(plug1==1&&plug2==2) //左插头为1插头 上插头为2插头 匹配 直接转移 { if(i==lasx&&j==lasy) { if((change(change(state,m,0),m+1,0))==0) ans+=f[pre][state]; //累加 ok=1; continue; } } else if(plug1==1&&plug2==1) //左插头为1插头 上插头为1插头 没有这种情况 continue; else if(plug1==2&&plug2==2) //左插头为2插头 上插头为2插头 更新状态 { int p=update1(state,0,m); p=change(change(p,m,0),m+1,0); f[cur][p]+=f[pre][state]; insert(cur,p); } else if(plug1!=0&&plug2==0) //左插头为1/2插头 无上插头 直接转移 { f[cur][state]+=f[pre][state]; insert(cur,state); } else if(plug1==0&&plug2!=0) //无左插头 上插头为1/2插头 删掉最高位然后更改 { int kind=plug2; f[cur][change(change(state,m+1,0),m,kind)]+=f[pre][state]; insert(cur,change(change(state,m+1,0),m,kind)); } else if(plug1==0&&plug2==0) //不存在 continue; } } else //为中间的格子 { int plug1=get(state,j),plug2=get(state,j+1); if(A[i][j]==0) //障碍 { if(plug1==0&&plug2==0) { f[cur][state]+=f[pre][state]; insert(cur,state); } } else //不是障碍 { if(plug1==0&&plug2==0) //无插头 { f[cur][change(change(state,j+1,2),j,1)]+=f[pre][state]; //新建向右的2插头和向下的1插头 insert(cur,change(change(state,j+1,2),j,1)); } else if(plug1==1&&plug2==2) //左插头为1插头 上插头为2插头 更新状态 { if(i==lasx&&j==lasy) { ans+=f[pre][state]; ok=1; continue; } } else if(plug1==1&&plug2==1) //左插头为1插头 上插头为1插头 { int p=update2(state,0,j+1); p=change(change(p,j,0),j+1,0); f[cur][p]+=f[pre][state]; insert(cur,p); } else if(plug1==2&&plug2==1) //左插头为2插头 上插头为1插头 { f[cur][change(change(state,j,0),j+1,0)]+=f[pre][state]; insert(cur,change(change(state,j,0),j+1,0)); } else if(plug1==2&&plug2==2) //左插头为2插头 上插头为2插头 { int p=update1(state,0,j); p=change(change(p,j,0),j+1,0); f[cur][p]+=f[pre][state]; insert(cur,p); } else if(plug1>0&&plug2==0) //左插头为1/2插头 无上插头 { int kind=plug1; f[cur][state]+=f[pre][state]; //新建向下的1/2插头 f[cur][change(change(state,j,0),j+1,kind)]+=f[pre][state]; //新建向右的1/2插头 insert(cur,state); insert(cur,change(change(state,j,0),j+1,kind)); } else if(plug1==0&&plug2>0) //无左插头 上插头为1/2插头 { int kind=plug2; f[cur][change(change(state,j+1,0),j,kind)]+=f[pre][state]; //新建向下的1/2插头 f[cur][state]+=f[pre][state]; //新建向右的1/2插头 insert(cur,state); insert(cur,change(change(state,j+1,0),j,kind)); } } } } } } printf("%lld\n",ans); return 0; }