题意:平面上有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; }