zoukankan      html  css  js  c++  java
  • 斯坦纳树总结

    这个东西很久以前就学过了,不过前几天看到后又一脸懵逼,于是赶紧滚来复习一下。

    简介

    斯坦纳树是将图的一个指定点集内的所有点连通的一棵树,常见的问题有最小斯坦纳树(Minimal Steiner Tree)(怎么也叫MST啊喂)。和最小生成树不同的是,斯坦纳树可以包含不在指定点集内的点。

    求解方法

    斯坦纳树的求解方法类似于状压DP,设(f[S][i])表示以结点(i)为根,连通了点集(S)内的所有点的最小代价。

    先在外层枚举连通状态(S)

    然后转移分为两部分:

    1. 枚举(S)的子集进行转移:$$f[S][i]=min_{T subseteq S}{f[T][i]+f[S-T][i]}$$

    2. 使用SPFA松弛连通状态为(S)的的所有状态:$$f[S][i]=min(f[S][i],f[S][j]+e[j][i])$$

    例题:[BZOJ2595][WC2008]游览计划

    分析

    斯坦纳树输出方案,DP的时候顺便记个(pre[S][i][j])就好了。

    代码

    #include <bits/stdc++.h>
    #define rin(i,a,b) for(int i=(a);i<=(b);i++)
    #define irin(i,a,b) for(int i=(a);i>=(b);i--)
    #define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    int n,m,tot,val[15][15],pos[15][2],f[1<<10][15][15],pre[1<<10][15][15][3];
    int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};
    bool book[15][15],ans[15][15];
    std::queue<int> q;
    
    void spfa(int s){
    	while(!q.empty()){
    		int x=q.front()/20,y=q.front()%20;q.pop();
    		rin(i,1,4){
    			int xx=x+dx[i],yy=y+dy[i];
    			if(xx<1||xx>n||yy<1||yy>m) continue;
    			if(f[s][xx][yy]>f[s][x][y]+val[xx][yy]){
    				f[s][xx][yy]=f[s][x][y]+val[xx][yy];
    				pre[s][xx][yy][0]=s;
    				pre[s][xx][yy][1]=x;
    				pre[s][xx][yy][2]=y;
    				if(!book[xx][yy]){
    					q.push(xx*20+yy);
    					book[xx][yy]=1;
    				}
    			}
    		}
    		book[x][y]=0;
    	}
    }
    
    void getsteiner(){
    	memset(f,0x3f,sizeof f);
    	rin(i,1,tot) f[1<<(i-1)][pos[i][0]][pos[i][1]]=0;
    	rin(s,0,(1<<tot)-1){
    		rin(i,1,n) rin(j,1,m){
    			for(int ss=s;ss;ss=((ss-1)&s)){
    				if(f[s][i][j]>f[ss][i][j]+f[s^ss][i][j]-val[i][j]){
    					f[s][i][j]=f[ss][i][j]+f[s^ss][i][j]-val[i][j];
    					pre[s][i][j][0]=ss;
    					pre[s][i][j][1]=i;
    					pre[s][i][j][2]=j;
    				}
    			}
    			if(f[s][i][j]<1e9){
    				q.push(i*20+j);
    				book[i][j]=1;
    			}
    		}
    		spfa(s);
    	}
    }
    
    void getmap(int s,int x,int y){
    	if(!s||!x) return;
    	ans[x][y]=1;
    	getmap(pre[s][x][y][0],pre[s][x][y][1],pre[s][x][y][2]);
    	getmap(s^pre[s][x][y][0],pre[s][x][y][1],pre[s][x][y][2]);
    }
    
    int main(){
    	n=read(),m=read();
    	rin(i,1,n) rin(j,1,m){
    		val[i][j]=read();
    		if(!val[i][j]){
    			tot++;
    			pos[tot][0]=i;
    			pos[tot][1]=j;
    		}
    	}
    	getsteiner();
    	printf("%d
    ",f[(1<<tot)-1][pos[1][0]][pos[1][1]]);
    	getmap((1<<tot)-1,pos[1][0],pos[1][1]);
    	rin(i,1,n){
    		rin(j,1,m){
    			if(ans[i][j]){
    				if(val[i][j]) putchar('o');
    				else putchar('x');
    			}
    			else putchar('_');
    		}
    		putchar('
    ');
    	}
    	return 0;
    }
    

    [THUSC2017]巧克力

    分析

    斯坦纳树和二分答案(中位数的套路)的话很显然,但是那个随机化是真有点想不到。

    直接枚举是哪(k)种图案然后跑斯坦纳树的复杂度是(O(T log n inom{n imes m}{k} (3^k+2^k imes SPFA))),肯定会直接废掉(虽然好像也有不少分)。

    考虑上网搜题解,题解告诉我们可以给每种颜色随机一个(1 sim k)的新颜色然后再跑斯坦纳树,(k=5)时,这样单次的正确率是(frac{5!}{5^5}=0.0384),重复做(100)次的错误率就是(0.01992716266102454756995285049955)。博主向来非酋,所以做了(200)次。

    剩下的就是斯坦纳树模板题了,时间复杂度是(O(T log n imes 非酋常数 imes 100 imes (3^k+2^k imes SPFA)))

    代码

    #include <bits/stdc++.h>
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int AREA=240;
    int n,m,k,siz,cnt,c[AREA][AREA],a[AREA][AREA],b[AREA];
    int w[AREA][AREA],g[AREA],f[32][AREA][AREA];
    int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
    bool book[AREA][AREA],flag;
    std::queue<int> q;
    
    void spfa(int s){
    	while(!q.empty()){
    		int x=q.front()/1000,y=q.front()%1000;q.pop();
    		rin(i,0,3){
    			int xx=x+dx[i],yy=y+dy[i];
    			if(xx<1||xx>n||yy<1||yy>m) continue;
    			if(f[s][xx][yy]>f[s][x][y]+w[xx][yy]){
    				f[s][xx][yy]=f[s][x][y]+w[xx][yy];
    				if(!book[xx][yy]) q.push(xx*1000+yy),book[xx][yy]=true;
    			}
    		}
    		book[x][y]=false;
    	}
    }
    
    int getsteiner(){
    	rin(s,0,(1<<k)-1) rin(i,1,n) rin(j,1,m) f[s][i][j]=1e9;
    	rin(i,1,n) rin(j,1,m) if(c[i][j]>0) f[1<<(g[c[i][j]]-1)][i][j]=w[i][j];
    	rin(s,1,(1<<k)-1){
    		rin(i,1,n) rin(j,1,m){
    			for(register int ss=s;ss;ss=((ss-1)&s))
    				f[s][i][j]=std::min(f[s][i][j],f[ss][i][j]+f[s^ss][i][j]-w[i][j]);
    			if(f[s][i][j]<1e9) q.push(i*1000+j),book[i][j]=true;
    		}
    		spfa(s);
    	}
    	int ret=1e9;
    	rin(i,1,n) rin(j,1,m) ret=std::min(ret,f[(1<<k)-1][i][j]);
    	return ret;
    }
    
    bool check(int mid){
    	int temp=1e9;
    	rin(i,1,200){
    		rin(i,1,cnt) g[i]=rand()%k+1;
    		rin(i,1,n) rin(j,1,m) w[i][j]=c[i][j]>0?(a[i][j]>mid?2001:1999):1e9;
    		temp=std::min(temp,getsteiner());
    	}
    	if(temp>=1e9){flag=true;printf("-1 -1
    ");return 0;}
    	bool ret=((temp+n*m)/2000)*2000>=temp;
    	if(mid==((1+siz)>>1)) printf("%d ",(temp+n*m)/2000);
    	return ret;
    }
    // if return value is true, the answer will be equal to or less than mid
    // else the answer will be greater than mid
    
    int main(){
    	srand((int)19260817);
    	int T=read();
    	while(T--){
    		n=read(),m=read(),k=read();siz=0;flag=false;
    		rin(i,1,n) rin(j,1,m) (c[i][j]=read())>0?(b[++siz]=c[i][j]):0;
    		rin(i,1,n) rin(j,1,m) a[i][j]=read();
    		std::sort(b+1,b+siz+1);siz=std::unique(b+1,b+siz+1)-b-1;
    		rin(i,1,n) rin(j,1,m) if(c[i][j]>0) c[i][j]=std::lower_bound(b+1,b+siz+1,c[i][j])-b;
    		cnt=siz,siz=0;
    		rin(i,1,n) rin(j,1,m) b[++siz]=a[i][j];
    		std::sort(b+1,b+siz+1);siz=std::unique(b+1,b+siz+1)-b-1;
    		rin(i,1,n) rin(j,1,m) a[i][j]=std::lower_bound(b+1,b+siz+1,a[i][j])-b;
    		int l=1,r=siz,ans;
    		while(l<=r){
    			int mid=((l+r)>>1);
    			if(check(mid)) ans=mid,r=mid-1;
    			else l=mid+1;
    			if(flag) break;
    		}
    		if(!flag) printf("%d
    ",b[ans]);
    	}
    }
    
  • 相关阅读:
    JS收集<3>:限制URL
    TSQL小收集<1>:为已经存在的表添加唯一约束
    第一个NHibernate实例 yangan
    用Javascript获取select的值 yangan
    获取页面名称语句 yangan
    asp.net中当服务器出错时显示指定的错误页面,同时把错误信息写入系统日志文件 yangan
    Log4Net使用指南 yangan
    明确自己的首要责任
    《大数据时代》摘录
    2013年3月阅读链接
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10289987.html
Copyright © 2011-2022 走看看