[CF277E]Binary Tree on Plane

题意

给你平面上 \(n\) 个点 (\(2 \leq n \leq 400\)),要求用这些点组成一个二叉树(每个节点的儿子节点不超过两个),定义每条边的权值为两个点之间的欧几里得距离。求一个权值和最小的二叉树,并输出这个权值。

其中,点 \(i\) 可以成为点 \(j\) 的的父亲的条件是:点 \(i\)\(y\) 坐标比 \(j\)\(y\) 坐标大。

如果不存在满足条件的二叉树,输出 \(-1\)

思路

题目给出了对每个点的度数的限制,并且父亲和孩子不能随便取,考虑费用流。

把每个点拆成两个点 \(p1\)\(p2\),分别代表入点和出点。由于每个点最多只能有两个子节点,所以从源点向 \(p1\) 连一条容量为 \(2\) ,费用为 \(0\) 的边;由于每个点只有一个父节点,所以从 \(p2\) 向汇点连一条容量为 \(1\) ,费用为 \(0\) 边。同时,如果一个点能连向另一个点,就从该点的 \(p1\) 向那个点的 \(p2\) 连一条容量为 \(1\),费用为这两点之间的距离 \(len\) 的边。最后从源点到汇点跑费用流即可。

代码

#include<bits/stdc++.h>
#define MAXN 410
#define INF 2000000000
using namespace std;
int n,s,t;
struct point
{
    int x,y;
}po[MAXN];
bool cmp(point a,point b)
{
    return a.y<b.y;
}
struct edge
{
    int to,from,nxt,w;
    double c;
}ed[MAXN*MAXN];
int head[MAXN],tot=1;
void add(int u,int v,int w,double c)
{
    ed[++tot].to=v;
    ed[tot].w=w;
    ed[tot].c=c;
    ed[tot].from=u;
    ed[tot].nxt=head[u];
    head[u]=tot;
}
int pre[MAXN],vis[MAXN],flo[MAXN];
int arr[MAXN],up=0,down=0,now;
int flow;
double cost;
double dis[MAXN];
bool spfa()
{
    up=down=0;
    arr[++down]=s;
    for(int i=0;i<=n*2+1;i++)
        dis[i]=INF;
    dis[s]=0;
    vis[s]=1;
    flo[s]=INF;
    while(up<down)
    {
        now=arr[++up];
        vis[now]=0;
        for(int i=head[now];i;i=ed[i].nxt)
        {
            int v=ed[i].to;
            double c=ed[i].c;
            if(!ed[i].w)continue;
            if(dis[v]>dis[now]+c)
            {
                dis[v]=dis[now]+c;
                flo[v]=min(flo[now],ed[i].w);
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=1;
                    arr[++down]=v;
                }
            }
        }
    }
    if(dis[t]<INF)return true;
    else return false;
}
void costflow()
{
    flow=cost=0;
    int now,last;
    while(spfa())
    {
        flow+=flo[t];
        cost+=dis[t];
        now=t;
        while(now!=s)
        {
            last=pre[now];
            ed[last].w-=flo[t];
            ed[last^1].w+=flo[t];
            now=ed[last].from;
        }
    }
}
double getdis(point a,point b)
{
    return sqrt((a.x-b.x+0.0)*(a.x-b.x+0.0)+(a.y-b.y+0.0)*(a.y-b.y+0.0));
}
int main()
{
    scanf("%d",&n);
    s=0;
    t=2*n+1;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&po[i].x,&po[i].y);
    sort(po+1,po+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        add(s,i,2,0);
        add(i,s,0,0);
        add(i+n,t,1,0);
        add(t,i+n,0,0);
        for(int j=1;j<i;j++)
        {
            if(po[j].y<po[i].y)
            {
                add(i,j+n,1,getdis(po[i],po[j]));
                add(j+n,i,1,-getdis(po[i],po[j]));
            }
        }
    }
    costflow();
    if(flow==n-1)printf("%lf",cost);
    else printf("-1");
    return 0;
}
 posted on 2022-10-25 14:42  hu_led  阅读(19)  评论(0编辑  收藏  举报