Codeforces Round #170 (Div. 1) E.Binary Tree on Plane

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值