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

    斯坦纳树

    就是一个很暴力的东西。考虑要做最小生成树,其中一些点必须选,一些点可选可不选。必选点比较少,可以用状压维护。

    按照状压状态从小到大更新,每次先枚举子集更新自己,再跑最短路更新全局。

    复杂度(O(n 3^n)),感觉特弱智。

    WC2008 游览计划

    img

    对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,216]的范围内

    题解

    此题就是斯坦纳树板子题,没什么好说的。重点在于理解更新顺序。

    #include<bits/stdc++.h>
    #define co const
    #define il inline
    template<class T> T read(){
    	T x=0,w=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*w;
    }
    template<class T> il T read(T&x){
    	return x=read<T>();
    }
    using namespace std;
    typedef long long LL;
    
    co int N=11,S=1025,INF=0x3f3f3f3f;
    int n,m,tot;
    int a[N][N],f[N][N][S];
    co int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
    int vis[N][N];
    struct node{int x,y,s;}pre[N][N][S];
    
    queue<pair<int,int> > q;
    void spfa(int cur){
    	while(q.size()){
    		int x=q.front().first,y=q.front().second;
    		q.pop(),vis[x][y]=0;
    		for(int i=0;i<4;++i){
    			int nx=x+dx[i],ny=y+dy[i]; // edit 1:dy
    			if(nx<1||nx>n||ny<1||ny>m) continue;
    			if(f[nx][ny][cur]>f[x][y][cur]+a[nx][ny]){
    				f[nx][ny][cur]=f[x][y][cur]+a[nx][ny];
    				pre[nx][ny][cur]=(node){x,y,cur};
    				if(!vis[nx][ny]) q.push(make_pair(nx,ny)),vis[nx][ny]=1;
    			}
    		}
    	}
    }
    void dfs(int x,int y,int now){
    	vis[x][y]=1;
    	node t=pre[x][y][now];
    	if(!t.x&&!t.y) return;
    	dfs(t.x,t.y,t.s);
    	if(t.x==x&&t.y==y) dfs(t.x,t.y,now-t.s);
    }
    int main(){
    	read(n),read(m);
    	memset(f,0x3f,sizeof f);
    	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
    		if(!read(a[i][j])) f[i][j][1<<tot]=0,++tot;
    	int lim=(1<<tot)-1;
    	for(int sta=0;sta<=lim;++sta){
    		for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
    			for(int s=sta;s;s=(s-1)&sta)
    				if(f[i][j][s]+f[i][j][sta-s]-a[i][j]<f[i][j][sta]){
    					f[i][j][sta]=f[i][j][s]+f[i][j][sta-s]-a[i][j];
    					pre[i][j][sta]=(node){i,j,s};
    				}
    			if(f[i][j][sta]<INF) q.push(make_pair(i,j)),vis[i][j]=1;
    		}
    		spfa(sta);
    	}
    	int ansx,ansy;
    	for(int i=1,flag=0;i<=n&&!flag;++i)
    		for(int j=1;j<=m;++j)if(!a[i][j]){
    			ansx=i,ansy=j,flag=1;break;
    		}
    	printf("%d
    ",f[ansx][ansy][lim]);
    	memset(vis,0,sizeof vis);
    	dfs(ansx,ansy,lim);
    	for(int i=1;i<=n;++i,puts(""))
    		for(int j=1;j<=m;++j){
    			if(!a[i][j]) putchar('x');
    			else vis[i][j]?putchar('o'):putchar('_');;
    		}
    	return 0;
    }
    

    BZOJ4774 修路

    村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。

    对于边带权的无向图 G = (V, E),请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边的权值和。

    1 <= d <= 4,2d <= n <= 104,0 <= m <= 104

    题解

    这题的特殊点不需要两两连通,所以求的是最小森林。

    还是考虑状压,(f(S,i))表示状态为(S)的点集连成的生成树的根为(i)的最小代价,用斯坦纳树做法转移即可。

    每次用(min_i f(S,i))的值更新(g(S)),最后把合法状态的(g)进行枚举子集转移即可。

    #include<bits/stdc++.h>
    #define co const
    #define il inline
    template<class T> T read(){
    	T x=0,w=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*w;
    }
    template<class T> il T read(T&x){
    	return x=read<T>();
    }
    using namespace std;
    typedef long long LL;
    
    co int N=10000+1,S=1<<8,INF=0x3f3f3f3f;
    int n,m,D;
    vector<int> to[N],we[N];
    int f[S][N],g[S];
    bool vis[N];
    queue<int> q;
    void spfa(int f[]){
    	while(q.size()){
    		int x=q.front();
    		q.pop(),vis[x]=0;
    		for(int i=0;i<(int)to[x].size();++i){
    			int y=to[x][i],w=we[x][i];
    			if(f[y]>f[x]+w){
    				f[y]=f[x]+w;
    				if(!vis[y]) q.push(y),vis[y]=1;
    			}
    		}
    	}
    }
    il bool check(int s){
    	return (s&((1<<D)-1))==(s>>D);
    }
    int main(){
    	read(n),read(m),read(D);
    	while(m--){
    		int x=read<int>(),y=read<int>(),w=read<int>();
    		to[x].push_back(y),we[x].push_back(w);
    		to[y].push_back(x),we[y].push_back(w);
    	}
    	memset(f,0x3f,sizeof f),memset(g,0x3f,sizeof g);
    	for(int i=1;i<=D;++i)
    		f[1<<(i-1)][i]=f[1<<(D+i-1)][n-i+1]=0;
    	int lim=(1<<(D<<1))-1;
    	for(int i=0;i<=lim;++i){
    		for(int j=1;j<=n;++j){
    			for(int k=i&(i-1);k;k=(k-1)&i)
    				f[i][j]=min(f[i][j],f[k][j]+f[i^k][j]);
    			if(f[i][j]<INF) q.push(j),vis[j]=1;
    		}
    		spfa(f[i]);
    		for(int j=1;j<=n;++j) g[i]=min(g[i],f[i][j]);
    	}
    	for(int i=0;i<=lim;++i)
    		for(int t=(i-1)&i;t;t=(t-1)&i)
    			if(check(t)&&check(i^t)) g[i]=min(g[i],g[t]+g[i^t]);
    	printf("%d
    ",g[lim]<INF?g[lim]:-1);
    	return 0;
    }
    
  • 相关阅读:
    「考试」省选27
    「考试」省选26
    「考试」省选25
    $dy$讲课总结
    「笔记」$Min\_25$筛
    「考试」省选24
    「总结」多项式生成函数例题(4)
    「总结」多项式生成函数相关(4)
    「考试」省选23
    「总结」后缀3
  • 原文地址:https://www.cnblogs.com/autoint/p/steiner_tree.html
Copyright © 2011-2022 走看看