CF277E Binary Tree on Plane

CF277E Binary Tree on Plane

题目大意

给定平面上的 \(n\) 个点,定义两个点之间的距离为两点欧几里得距离,求最小二叉生成树。

题解

妙啊。

难点在于二叉的限制。

注意到二叉树每一个点最多有一个父亲,最多可以有两个儿子,这让我们联想到了网络流中的容量。

考虑建图:

令源点为 \(s\),汇点为 \(t\)

我们将每一个点 \(u\) 拆为两个点 \(u_1,u_2\);

每个点最多可以有两个儿子 \(\Rightarrow\)\(s\)\(u_1\) 连一条容量为 \(2\),费用为 \(0\) 的边。

每个点最多可以有一个父亲 \(\Rightarrow\)\(u_2\)\(t\) 连一条容量为 \(1\),费用为 \(0\) 的边。

\(u\) 可以成为 \(v\) 的父亲 \(\Rightarrow\)\(u_1\)\(v_2\) 连一条容量为 \(1\),费用为 \(\operatorname{dist}(u,v)\) 的边。

然后我们跑最小费用最大流,判断最大流是否为 \(n-1\) 即可。

/*---Author:HenryHuang---*/
#include<bits/stdc++.h>
#define eps 1e-6
using namespace std;
const int maxn=800+5;
typedef long long ll;
struct edge{
	int to,nex,w;
	double v;
}e[maxn*maxn*2];
int head[maxn],cur[maxn],tot=1;
void add(int a,int b,int c,double d){
	e[++tot]=(edge){b,head[a],c,d};
	head[a]=cur[a]=tot;
}
void addedge(int a,int b,int c,double d){
	add(a,b,c,d);
	add(b,a,0,-d);
}
int n,m,s,t;
double dis[maxn];
bool vis[maxn];
bool spfa(){
	memset(vis,0,sizeof vis);
	for(int i=s;i<=t;++i) vis[i]=0,dis[i]=(1ll<<60),cur[i]=head[i];
	queue<int> Q;
	dis[s]=0;vis[s]=1;Q.push(s);
	while(!Q.empty()){
		int u=Q.front();Q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].to;
			if(e[i].w&&dis[v]-dis[u]-e[i].v>eps){
				dis[v]=dis[u]+e[i].v;
				if(!vis[v]) vis[v]=1,Q.push(v);
			}
		}
	}
	return dis[t]<(double)(1ll<<60);
}
int dfs(int u,int in){
	if(u==t) return in;
	int out=0,tmp;
	vis[u]=1;
	for(int i=cur[u];i;i=e[i].nex){
		int v=e[i].to;cur[u]=i;
		if((!vis[v])&&e[i].w&&abs(dis[v]-dis[u]-e[i].v)<eps&&(tmp=dfs(v,min(in,e[i].w)))){
			e[i].w-=tmp,e[i^1].w+=tmp;
			in-=tmp,out+=tmp;
			if(!in) break;
		}
	}
	if(!out) dis[u]=0;
	vis[u]=0;
	return out;
}
double x[maxn],y[maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i) cin>>x[i]>>y[i];
	s=0,t=2*n+1;
	for(int i=1;i<=n;++i) addedge(s,i,2,0),addedge(i+n,t,1,0);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(i!=j&&y[i]>y[j]) addedge(i,j+n,1,sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
	int ans1=0;double ans2=0;	
	while(spfa()){
		int tmp=dfs(s,2e9);
		ans2+=dis[t]*tmp;
		ans1+=tmp;
	}
	if(ans1==n-1) cout<<setprecision(6)<<fixed<<ans2<<'\n';
	else cout<<-1<<'\n';
	return 0;
}
posted @ 2020-06-05 21:51  Henry__Huang  阅读(155)  评论(0编辑  收藏  举报