[模板] 斯坦纳树

斯坦纳树

斯坦纳树解决的是这样的一类问题:

在有边权/点权无向图上找到总权值最小的子图, 使得给定的关键点互相连通.

容易发现得到的子图会是一棵树.

//to upd

代码

//luogu4294 [WC2008]游览计划
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//---------------------------------------
const int nsz=15,psz=150,k2sz=1050,ninf=1e3;
int n,m,k,bnd,val[nsz][nsz],isedge[nsz][nsz],choose[nsz][nsz];
int dir[5][2]{{1,0},{-1,0},{0,1},{0,-1}};

int dp[k2sz][nsz][nsz];

struct tpl{int x,y;}emp[nsz];
struct tdis{tpl p;int d;};
bool operator<(const tdis &a,const tdis &b){return a.d>b.d;}
priority_queue<tdis> pq;
int curs;

pair<int,tpl> pre[k2sz][nsz][nsz];
int vi[nsz][nsz];
void dij(){
	rep(i,1,n)rep(j,1,m)vi[i][j]=0;
	int cnt=0,cntt=n*m,d;
	tpl u;
	while(!pq.empty()&&cnt<cntt){
		u=pq.top().p,d=pq.top().d,pq.pop();
		if(vi[u.x][u.y])continue;
		vi[u.x][u.y]=1,++cnt;
		rep(i,0,3){
			int x1=u.x+dir[i][0],y1=u.y+dir[i][1];
			int tmp=val[x1][y1]+d;
			if(!isedge[x1][y1]&&dp[curs][x1][y1]>tmp){
				dp[curs][x1][y1]=tmp;
				pre[curs][x1][y1]=make_pair(curs,(tpl){u.x,u.y});
				pq.push((tdis){(tpl){x1,y1},tmp});
			}
		}
	}
}

void dfs(int p,int x,int y){
	if(p==0)return;
	choose[x][y]=1;
	auto tmp=pre[p][x][y];
	dfs(tmp.first,tmp.second.x,tmp.second.y);
	if(tmp.first&&tmp.first!=p)dfs(p^tmp.first,tmp.second.x,tmp.second.y);
}

int sol(){
	bnd=(1<<k)-1;
	rep(i,1,bnd)rep(a,1,n)rep(b,1,m)dp[i][a][b]=ninf;
	rep(i,1,k)dp[1<<(i-1)][emp[i].x][emp[i].y]=0;
	rep(i,1,bnd){
		curs=i;
		priority_queue<tdis>().swap(pq);
		rep(a,1,n){
			rep(b,1,m){
				int &cur=dp[i][a][b];
				for(int j=(i-1)&i;j;j=(j-1)&i){
					int tmp=dp[j][a][b]+dp[i^j][a][b]-val[a][b];
					if(cur>tmp)cur=tmp,pre[i][a][b]=make_pair(j,(tpl){a,b});
				}
				if(cur<ninf)pq.push((tdis){(tpl){a,b},cur});
			}
		}
//		printf("s=%d\n",i);
//		rep(a,1,n){
//			rep(b,1,m)printf("%d ",dp[i][a][b]);
//			printf("\n");
//		}
//		printf("\n");
		dij();
//		printf("res\n");
//		rep(a,1,n){
//			rep(b,1,m)printf("%d ",dp[i][a][b]);
//			printf("\n");
//		}
//		printf("\n");
//		printf("pre\n");
//		rep(a,1,n){
//			rep(b,1,m)printf("<%d %d %d> ",pre[i][a][b].first,pre[i][a][b].second.x,pre[i][a][b].second.y);
//			printf("\n");
//		}
//		printf("\n");

	}
	int res=ninf,resx,resy;
	rep(i,1,n)rep(j,1,m)if(dp[bnd][i][j]<res)res=dp[bnd][i][j],resx=i,resy=j;
	dfs(bnd,resx,resy);
	return res;
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	rep(i,0,n+1)isedge[i][0]=isedge[i][m+1]=1;
	rep(i,1,m)isedge[0][i]=isedge[n+1][i]=1;
	rep(i,1,n){
		rep(j,1,m){
			cin>>val[i][j];
			if(val[i][j]==0)emp[++k]=(tpl){i,j};
		}
	}
	cout<<sol()<<'\n';
	rep(i,1,n){
		rep(j,1,m){
			if(val[i][j]==0)cout<<'x';
			else if(choose[i][j])cout<<'o';
			else cout<<'_';
		}
		cout<<'\n';
	}
	return 0;
}
posted @ 2019-07-10 21:36  Ubospica  阅读(228)  评论(0编辑  收藏  举报