/* ural1519 求经过所有可行点的哈密顿回路的个数 括号匹配法,转移有点复杂,但是时间空间比较小 */ #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<algorithm> #include<cmath> #include<map> #include<queue> #define LL long long using namespace std; const int maxn=30001; int n,m,now,pre; int mov[13]={0,2,4,6,8,10,12,14,16,18,20,22,24};//根据进制选择移位距离 char gp[20][20],fx,fy;//存图,最后一个可行点的坐标 inline int getbit(LL st,int k){ return (st>>mov[k])&3;}//获得第k位的状态 inline int pybit(LL st,int k){ return st<<mov[k];} //平移k位 inline LL clrbit(LL st,int i,int j){ return st&(~(3<<mov[i]))&(~(3<<mov[j]));}//清空第i位和第j位 struct node//状态离散hash { int head[maxn],next[maxn],size; LL sum[maxn],sta[maxn];//保存所求和及状态 void clear() { memset(head,-1,sizeof(head)); memset(sum,0,sizeof(sum)); size=0; } void push(LL st,const LL v) { LL hash=st%maxn; for(int i=head[hash];i>=0;i=next[i]) { if(sta[i]==st) { sum[i]+=v; return; } } sta[size]=st,sum[size]=v; next[size]=head[hash],head[hash]=size++; } }dp[2]; inline int fl(LL st,int pos)//从左往右找到和当前pos位置匹配的右括号 { int cnt=1; for(int i=pos+1;i<=m;i++) { int k=getbit(st,i); if(k==1) cnt++; else if(k==2) cnt--; if(!cnt) return i; } } inline int fr(LL st,int pos)//从右往左找到和当前pos位置匹配的左括号 { int cnt=1; for(int i=pos-1;i>=0;i--) { int k=getbit(st,i); if(k==2) cnt++; else if(k==1) cnt--; if(!cnt) return i; } } void DP(int x,int y,int k)//每种状态的转移,根据需要修改 { int l=getbit(dp[pre].sta[k],y-1); int up=getbit(dp[pre].sta[k],y); LL st=clrbit(dp[pre].sta[k],y-1,y); LL v=dp[pre].sum[k]; if(!l&&!up) { if(gp[x][y]=='*') { dp[now].push(st,v); return; } if(x<n&&y<m&&gp[x+1][y]=='.'&&gp[x][y+1]=='.') dp[now].push(st|pybit(1,y-1)|pybit(2,y),v); } else if(!l||!up) { int e=l+up; if(x<n&&gp[x+1][y]=='.') dp[now].push(st|pybit(e,y-1),v); if(y<m&&gp[x][y+1]=='.') dp[now].push(st|pybit(e,y),v); } else if(l==1&&up==1) dp[now].push(st^pybit(3,fl(st,y)),v); else if(l==2&&up==2) dp[now].push(st^pybit(3,fr(st,y-1)),v); else if(l==2&&up==1) dp[now].push(st,v); else if(x==fx&&y==fy) dp[now].push(st,v); } LL solve() { dp[0].clear();//初状态 dp[0].push(0,1); now=0,pre=1; for(int i=1;i<=n;i++)//逐格逐状态枚举 { pre=now,now^=1,dp[now].clear(); for(int k=0;k<dp[pre].size;k++)//轮廓线下移对齐 dp[now].push(pybit(dp[pre].sta[k],1),dp[pre].sum[k]); for(int j=1;j<=m;j++) { pre=now,now^=1,dp[now].clear(); for(int k=0;k<dp[pre].size;k++) { DP(i,j,k); } } } for(int i=0;i<dp[now].size;i++)//寻找最终答案 if(dp[now].sta[i]==0) return dp[now].sum[i]; return 0; } int main() { while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++)//都是从1开始的 scanf("%s",&gp[i][1]); fx=0; for(int i=n;i>0&&!fx;i--)//寻找最后一个可行点 { for(int j=m;j>0&&!fx;j--) { if(gp[i][j]=='.') fx=i,fy=j; } } if(fx==0) puts("0"); else cout<<solve()<<endl; } return 0; } /* ural1519 求经过所有可行点的哈密顿回路的个数 最小表示法 转移简单,时间空间较大 */ #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #define LL long long using namespace std; const int maxn=30001,inc=3,bit=7;//3位二进制以及111的表示 int n,m,now,pre,code[20],bin[20],res[20];//用来表示状态的每一位的数值 char gp[20][20],fx,fy;//图和最后的可行点 struct node//离散化hash { int head[maxn],next[maxn],size; LL sum[maxn],sta[maxn]; void clear() { memset(head,-1,sizeof(head)); size=0; } void push(LL st,const LL v) { LL hash=st%maxn; for(int i=head[hash];i>=0;i=next[i]) { if(sta[i]==st) { sum[i]+=v; return ; } } sta[size]=st,sum[size]=v; next[size]=head[hash],head[hash]=size++; } }dp[2]; inline LL encode(int m)//将code转换成状态 { LL st=0; int cnt=1; memset(bin,-1,sizeof(bin)); bin[0]=0; for(int i=m;i>=0;i--) { if(bin[code[i]]==-1) bin[code[i]]=cnt++; code[i]=bin[code[i]]; st<<=inc; st|=code[i]; } return st; } inline void decode(LL st,int m)//将状态转换成code { for(int i=0;i<=m;i++) { code[i]=st&bit; st>>=inc; } } void DP(int x,int y,int k)//dp具体情况具体分析 { decode(dp[pre].sta[k],m); int l=code[y-1]; int up=code[y]; code[y-1]=code[y]=0; memcpy(res,code,sizeof(code)); LL v=dp[pre].sum[k]; if(!l&&!up) { if(gp[x][y]=='*') dp[now].push(encode(m),v); else if(x<n&&y<m&&gp[x+1][y]=='.'&&gp[x][y+1]=='.') { code[y]=code[y-1]=bit; dp[now].push(encode(m),v); } } else if(!l||!up) { int e=l+up; if(x<n&&gp[x+1][y]=='.') { code[y-1]=e; dp[now].push(encode(m),v); memcpy(code,res,sizeof(res)); } if(y<m&&gp[x][y+1]=='.') { code[y]=e; dp[now].push(encode(m),v); } } else if(l!=up) { for(int i=0;i<=m;i++) if(code[i]==up) code[i]=l; dp[now].push(encode(m),v); } else if(x==fx&&y==fy) dp[now].push(encode(m),v); } LL solve() { dp[0].clear();//初始化状态 dp[0].push(0,1); now=0,pre=1; for(int i=1;i<=n;i++)//逐格逐状态枚举转移 { pre=now,now^=1,dp[now].clear(); for(int k=0;k<dp[pre].size;k++)//轮廓线行转移 dp[now].push(dp[pre].sta[k]<<inc,dp[pre].sum[k]); for(int j=1;j<=m;j++) { pre=now,now^=1,dp[now].clear(); for(int k=0;k<dp[pre].size;k++) { DP(i,j,k); } } } for(int i=0;i<dp[now].size;i++) if(dp[now].sta[i]==0) return dp[now].sum[i]; return 0; } int main() { while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++)//都是从1开始 scanf("%s",&gp[i][1]); fx=fy=0; for(int i=n;i>0&&!fx;i--)//寻找最终的位置 for(int j=m;j>0&!fx;j--) if(gp[i][j]=='.') fx=i,fy=j; if(fx==0)puts("0"); else cout<<solve()<<endl; } } /* HDU 1964 Pipes 插头DP 每个格子之间的墙壁有一个花费,求用一个环经过每个格子一次的最小花费 */ #include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXD=15; const int HASH=30007;//一个比实际容量稍大的素数 const int STATE=1000010;//哈希表的最大元素个数 using namespace std; int N,M; int maze[MAXD][MAXD]; int code[MAXD]; int ch[MAXD];//最小表示法使用 struct number { int right,below; }node[12][12]; int ex,ey;//最后一个非障碍格子的坐标 struct HASHMAP { int head[HASH],next[STATE],size; long long state[STATE]; long long f[STATE]; void init() { size=0; memset(head,-1,sizeof(head));//用单独链表法处理碰撞 } void push(long long st,long long ans)//key->value { int i; int h=st%HASH; for(i=head[h];i!=-1;i=next[i])//这里要注意是next if(state[i]==st)//找到了此键值 { if(ans<f[i]) f[i]=ans; //键值已存在,在这种状态下只是把次数加进去就好啦 return; } state[size]=st; f[size]=ans; next[size]=head[h]; head[h]=size++; } }hm[2]; void decode(int *code,int m,long long st)//把某行上的轮廓信息解成一个code数组 { for(int i=m;i>=0;i--) { code[i]=st&7;//要是只有2中状态就&1呗 st>>=3; } } long long encode(int *code,int m)//最小表示法 m<=12显然只有6个不同的连通分量 { int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; long long st=0; for(int i=0;i<=m;i++) { if(ch[code[i]]==-1)ch[code[i]]=cnt++;//新发现一个 code[i]=ch[code[i]]; st<<=3;//0~7 8进制表示 st|=code[i];//<==>st+=code[i] } return st;//返回最终次轮廓上的连通分量信息 } void shift(int *code,int m)//当到最后一列的时候,相当于需要把code中所有元素向右移一位 { for(int i=m;i>0;i--)code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur)//cur是当前状态,操作之后就是cur^1啦 总共就三大种情况 逐个讨论一下就好 { int k,left,up; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]); left=code[j-1]; up=code[j]; if(left&&up) { if(left==up)//只能出现在最后一个非障碍格子 { if(i==ex&&j==ey) { code[j-1]=code[j]=0;//最终合并成一个回路 if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else//不在同一个连通分量则合并成同一个 { code[j-1]=code[j]=0; for(int t=0;t<=M;t++)//所谓的O(n)复杂度 if(code[t]==up) code[t]=left; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else if((left&&(!up))||((!left)&&up))//写的真墨迹 直接left||up就得了呗 右下没有插头则连出来一个 {//对于当前格子(i,j)code[j-1]是它左侧的格子插头信息,code[j]是它右边的格子插头信息 //处理后:code[j-1]是(i,j)下方格子插头信息,code[j]是~右边格子插头信息 int t; if(left)t=left; else t=up; if(maze[i][j+1])//右边没有障碍 { code[j-1]=0; code[j]=t; hm[cur^1].push(encode(code,M),hm[cur].f[k]+node[i][j].right); } if(maze[i+1][j])//下边没有障碍 { code[j-1]=t; code[j]=0; if(j==M)shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]+node[i][j].below); } } else//无插头,则构造新的连通块 { if(maze[i][j+1]&&maze[i+1][j]) { code[j-1]=code[j]=13;//只要是一个没出现过的就好,因为代入函数不涉及它到底是几 hm[cur^1].push(encode(code,M),hm[cur].f[k]+node[i][j].right+node[i][j].below); } } } } void dpblock(int i,int j,int cur)//一个障碍是不可能有向下和向右的插头的,那就设其为0 { int k; for(k=0;k<hm[cur].size;k++) { decode(code,M,hm[cur].state[k]);//解码 code[j-1]=code[j]=0; if(j==M)shift(code,M);//换行 hm[cur^1].push(encode(code,M),hm[cur].f[k]);//毕竟是向后走了一格 //把当前的数据cur=0压到另一个位置cur=1==>把当前的数据cur=1压到另一个位置cur=0 } } char str[40]; void init() { memset(maze,0,sizeof(maze)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) maze[i][j]=1; getchar(); gets(str);//puts(str); for(int i=1;i<N;i++) { gets(str);//puts(str); for(int j=2;j<2*M;j+=2) node[i][j/2].right=str[j]-'0';//printf("%d ",node[i][j/2]); gets(str);//puts(str); for(int j=1;j<2*M;j+=2) node[i][(j+1)/2].below=str[j]-'0';//printf("%d ",str[j]-'0'); // for(int j=1;j<=M;j++) printf("%d %d ",node[i][j].right,node[i][j].below); // printf("\n"); } gets(str);//puts(str); for(int j=2;j<2*M;j+=2) node[N][j/2].right=str[j]-'0'; gets(str);//puts(str); // for(int i=1;i<=N;i++){for(int j=1;j<=M;j++) printf("%d %d ",node[i][j].right,node[i][j].below);printf("\n");} ex=N,ey=M; } void solve() { int i,j,cur=0; long long ans=0; hm[cur].init();//cur=0 hm[cur].push(0,1);//加入没插头的状态cur=0 for(i=1;i<=N;i++) for(j=1;j<=M;j++) { hm[cur^1].init();//每到一个位置,把另一组清零 清空cur=1==>清空cur=0 dpblank(i,j,cur);//当前这个进行设置。计算cur=0==>计算cur=1 // dpblock(i,j,cur); cur^=1;//cur变成了另一个数cur=1==>变成了cur=0 } for(i=0;i<hm[cur].size;i++)//现在的cur要是放在循环里就是待计算的位置 ans+=hm[cur].f[i];//各种状态的和就是总的可能的方案数 printf("%I64d\n",ans-1); } int main() { //freopen("cin.txt","r",stdin); int t; scanf("%d",&t); while(t--) { scanf("%d%d",&N,&M); init(); solve(); } return 0; } /* 单插头路径 zoj 3213 Beautiful Meadow 求任意可行路径所能得到的最优值,格子不需要全部走. 起始点是可行点的任意点,所以需要单插头(3),由于情况较多,所以作为单插头模板 */ #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> #include<iostream> #define LL long long using namespace std; const int maxn=30001,INF=1<<30; int mov[10]={0,2,4,6,8,10,12,14,16,18}; struct node { int size,head[maxn],next[maxn]; LL sta[maxn],sum[maxn]; void clear() { memset(head,-1,sizeof(head)); size=0; } void push(LL st,const LL v) { LL hash=st%maxn; for(int i=head[hash];i>=0;i=next[i]) { if(sta[i]==st) { sum[i]=max(sum[i],v); return ; } } sta[size]=st,sum[size]=v; next[size]=head[hash],head[hash]=size++; } }dp[2]; inline int getbit(LL st,int k){return 3&(st>>mov[k]);} inline int pybit(LL st,int k){return st<<mov[k];} inline int clrbit(LL st,int a,int b){return st&(~(3<<mov[a]))&(~(3<<mov[b]));} int fl(LL st,int k,int m) { int cnt=1; for(int i=k+1;i<=m;i++) { int e=getbit(st,i); if(e==2) cnt--; else if(e==1) cnt++; if(!cnt) return i; } } int fr(LL st,int k) { int cnt=1; for(int i=k-1;i>=0;i--) { int e=getbit(st,i); if(e==2) cnt++; else if(e==1) cnt--; if(!cnt) return i; } } int count(LL st)//统计单插头的个数 { int cnt=0; while(st) { if(getbit(st,0)==3) cnt++; st>>=2; } return cnt; } int n,m,gp[20][20]; LL DP() { LL ans=-INF; dp[0].clear(); dp[0].push(0,0); int now=0,pre=1; for(int i=1;i<=n;i++) { pre=now,now^=1,dp[now].clear(); for(int j=0;j<dp[pre].size;j++) dp[now].push(dp[pre].sta[j]<<2,dp[pre].sum[j]); for(int j=1;j<=m;j++) { if(gp[i][j]!=0) ans=max(ans,(LL)gp[i][j]); pre=now,now^=1,dp[now].clear(); for(int k=0;k<dp[pre].size;k++) { LL l=getbit(dp[pre].sta[k],j-1); LL up=getbit(dp[pre].sta[k],j); LL st=clrbit(dp[pre].sta[k],j,j-1); LL v=dp[pre].sum[k]+gp[i][j]; if(!l&&!up) { dp[now].push(st,dp[pre].sum[k]); if(gp[i][j]==0) continue; if(i<n&&j<m&&gp[i+1][j]&&gp[i][j+1]) dp[now].push(st|pybit(1,j-1)|pybit(2,j),v); if(count(st)<=1) { if(i<n&&gp[i+1][j]) dp[now].push(st|pybit(3,j-1),v); if(j<m&&gp[i][j+1]) dp[now].push(st|pybit(3,j),v); } } else if(!l||!up) { int e=l+up; if(i<n&&gp[i+1][j]) dp[now].push(st|pybit(e,j-1),v); if(j<m&&gp[i][j+1]) dp[now].push(st|pybit(e,j),v); if(e!=3&&count(st)<=1) { if(e==1) dp[now].push(st|pybit(3,fl(st,j,m)),v); else dp[now].push(st|pybit(3,fr(st,j-1)),v); } else if(e==3&&st==0) ans=max(ans,v); } else if(l==up) { if(l==1) dp[now].push(st^pybit(3,fl(st,j,m)),v); if(l==2) dp[now].push(st^pybit(3,fr(st,j-1)),v); if(l==3) ans=max(ans,v); } else if(l==2&&j==1) dp[now].push(st,v); else if(l==3||up==3) { int e=l==3?up:l; if(e==1) dp[now].push(st|pybit(3,fl(st,j,m)),v); if(e==2) dp[now].push(st|pybit(3,fr(st,j-1)),v); } } } } if(ans==-INF) ans=0; return ans; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&gp[i][j]); cout<<DP()<<endl; } return 0; } /* 广义路径 uva10572 black&white 求将一个棋盘染色,只能染黑和白,相同颜色必须联通,而且不能出现4格同颜色田字型 的方案数和打印其中一种方案. 最小表示法,两条轮廓线,一条表示联通性(m),另一条表示颜色状态(m+1). 是____————这种状态,不是之前的折线型. 根据颜色来转移联通性. */ #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<iostream> #include<algorithm> #define LL long long using namespace std; const int maxn=10007,bit=7,inc=3; int pre[9][9][maxn];//指向前一个状态 bool res[9][9][maxn];//记录当前状态那一格的颜色 int bin[20],code[20]; struct node { int size,head[maxn],next[maxn]; LL sta[maxn],clo[maxn],sum[maxn]; void clear() { memset(head,-1,sizeof(head)); size=0; } void push(LL st,const LL v,LL cs,int x,int y,bool cl,int k) { int hash=((st*13)+cs)%maxn; for(int i=head[hash];i>=0;i=next[i]) { if(sta[i]==st&&clo[i]==cs) { sum[i]+=v; return ; } } sta[size]=st,clo[size]=cs,sum[size]=v; res[x][y][size]=cl,pre[x][y][size]=k; next[size]=head[hash],head[hash]=size++; } }dp[2]; LL encode(int m)//注意从0开始,因为可能有m个不同颜色的插头 { LL st=0,cnt=0; memset(bin,-1,sizeof(bin)); for(int i=m-1;i>=0;i--) { if(bin[code[i]]==-1) bin[code[i]]=cnt++; code[i]=bin[code[i]]; st<<=inc; st|=code[i]; } return st; } void decode(LL st,int m) { for(int i=0;i<m;i++) { code[i]=st&bit; st>>=inc; } } int n,m,now,old; char gp[20][20]; bool check(LL cs,int x,int y,int m,int nc)//检查当前状态是否是两个状态的分界线 { int cnt=0,cnt1=0; for(int i=0;i<m;i++) { if(code[i]==code[y]) cnt++; if(((cs>>i)&1)==(nc^1)) cnt1++; } if(cnt==1) { if(cnt1>1) return false; char ch=nc==1?'o':'#'; for(int i=x-1;i<n;i++) for(int j=i==x-1?y+1:0;j<m;j++) { if(gp[i][j]==ch) return false; if(i+1<n&&j+1<m) return false; } } return true; } void DP(int x,int y,int nc) { for(int k=0;k<dp[old].size;k++) { bool l=y==0?0:((dp[old].clo[k]>>(y-1))&1)==nc; bool up=x==0?0:((dp[old].clo[k]>>y)&1)==nc; bool lp=(x==0||y==0)?0:((dp[old].clo[k]>>m)&1)==nc; decode(dp[old].sta[k],m); if(x&&!up&&!check(dp[old].clo[k],x,y,m,nc)) continue; if(!l&&!up&&!lp) code[y]=10; else if(l&&!up&&!lp) code[y]=code[y-1]; else if(!l&&up&&!lp) code[y]=code[y]; else if(!lp&&l&&up) { if(code[y-1]!=code[y]) { for(int i=0,id=code[y];i<m;i++) if(code[i]==id) code[i]=code[y-1]; } } else if(lp&&!up&&!l) { if(x==n-1&&y==m-1) continue; code[y]=10; } else if(lp&&l&&!up) code[y]=code[y-1]; else if(lp&&up&&!l) code[y]=code[y]; else continue; LL cs=dp[old].clo[k]&(~(1<<y))&(~(1<<m)); if(nc) cs|=1<<y; if((up==0&&nc==0)||(up==1&&nc==1)) cs|=1<<m; dp[now].push(encode(m),dp[old].sum[k],cs,x,y,nc,k); } } void slove() { dp[0].clear(); dp[0].push(0,1,0,n,m,0,-1); now=0,old=1; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { old=now,now^=1,dp[now].clear(); if(gp[i][j]!='#') DP(i,j,0); if(gp[i][j]!='o') DP(i,j,1); } int flag=-1,ans=0; for(int i=0;i<dp[now].size;i++)//统计合法状态 { decode(dp[now].sta[i],m); int cnt=0; memset(bin,-1,sizeof(bin)); for(int j=0;j<m;j++) { if(bin[code[j]]==-1) bin[code[j]]=cnt++; } if(cnt<=2) { flag=i; ans+=dp[now].sum[i]; } } //打印路径 if(flag==-1) puts("0\n"); else { printf("%d\n",ans); for(int i=n-1;i>=0;i--) for(int j=m-1;j>=0;j--) { gp[i][j]=res[i][j][flag]==0?'o':'#'; flag=pre[i][j][flag]; } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) printf("%c",gp[i][j]); puts(""); } puts(""); } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { scanf("%s",gp[i]); } slove(); } return 0; }
插头dp的几个模板
猜你喜欢
转载自blog.csdn.net/dllpxfire/article/details/80300593
今日推荐
周排行