题意:平面上有N个点,每个点u可以向其他v连边,只要点u的y坐标大于点v的y坐标,要求将这些点连成一棵二叉树,即树中的每个节点最多连出去两条边,问连成的二叉树的边权和最小为多少。
二分图的最小费用流,每个点拆成出点和入点,由超级源点到所有的入点建容量为2,费用为0的边,由所有的出点到超级汇点建容量为1,费用为0的边,当点u可以向点v连边的时候,将点u的入点向点v的出点连容量为1,费用为距离的边。最后判断如果流量不为n-1,那么就输出-1,否则输出最小费用。
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define MAXM 200001
#define Max(a, b) a > b ? a : b
#define Min(a, b) a < b ? a : b
#define INF (1<<30)
const int N=805;
struct Point
{
double x,y;
void get(){scanf("%lf%lf",&x,&y);}
bool operator<(const Point &b)const
{
return y>b.y;
}
}p[N];
double pow2(double x){return x*x;}
double calu(int a,int b)
{
return sqrt( pow2(p[a].x-p[b].x) + pow2(p[a].y-p[b].y) );
}
struct Edge
{
int from, to, val;
int next;
double cost;
Edge(){}
Edge(int a,int b,int c,double d,int e)
{ from=a; to=b; val=c; cost=d; next=e; }
}edge[MAXM];
int tot,head[MAXM],re_flow;
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void addEdge(int u, int v, int cap, double cost)
{
edge[tot]=Edge(u,v,cap,cost,head[u]);
head[u]=tot++;
edge[tot]=Edge(v,u,0,-cost,head[v]);
head[v]=tot++;
}
struct MinCostFlow
{
int visit[MAXM], pre[MAXM],que[MAXM], pos[MAXM];
double dist[MAXM];
int SPFA(int s, int t, int n)
{
int i, to, k;
for (i = 0; i <= n; i++)
{
pre[i] = -1; visit[i] = 0;
dist[i] = INF;
}
int front = 0, rear = 0;
que[rear++] = s, pre[s] = s, dist[s] = 0, visit[s] = 1;
while (front != rear)
{
int from = que[front++];
visit[from] = 0;
for (k = head[from]; k != -1; k = edge[k].next)
{
to = edge[k].to;
if (edge[k].val > 0 && dist[from]+edge[k].cost < dist[to])
{
dist[to] = dist[from] + edge[k].cost;
pre[to] = from;
pos[to] = k;
if (!visit[to])
{
visit[to] = 1;
que[rear++] = to;
}
}
}
}
if (pre[t] != -1 && dist[t] < INF)
return 1;
return 0;
}
void solve(int s, int t, int n)
{
int flow = 0;
double cost = 0;
while (SPFA(s, t, n))
{
int from,min = INF;
for (from = t; from != s; from = pre[from])
min = Min(min, edge[pos[from]].val);
flow += min;
cost += dist[t] * min;
for (from = t; from != s; from = pre[from])
{
edge[pos[from]].val -= min;
edge[pos[from]^1].val += min;
}
}
// cout<<"flow="<<flow<<endl;
if(flow!=re_flow) puts("-1");
else printf("%.6lf\n",cost);
}
}flow;
void input(int &st,int &ed)
{
int n; scanf("%d",&n);
for(int i=1;i<=n;i++) p[i].get();
sort(p+1,p+n+1);
st=0; ed=n+n+2;
for(int i=1;i<=n;i++)
{
addEdge(st,i,2,0);
addEdge(i+n,ed,1,0);
for(int j=i+1;j<=n;j++)
{
if(p[i].y>p[j].y) addEdge(i,j+n,1,calu(i,j));
}
}
re_flow=n-1;
}
int main()
{
init();
int st,ed;
input(st,ed);
flow.solve(st,ed,ed-st+1);
return 0;
}