Codeforces277 E. Binary Tree on Plane 最小费用最大流

Codeforces277 E. Binary Tree on Plane 最小费用最大流


传送门: https://codeforces.com/contest/277/problem/E

题意

给 你 平 面 上 n 个 点 ( 2 ≤ n ≤ 400 ) , 要 求 用 这 些 点 组 成 一 个 二 叉 树 。 给你平面上 n 个点 (2≤n≤400),要求用这些点组成一个二叉树。 n(2n400)
定 义 每 条 边 的 权 值 为 两 个 点 之 间 的 欧 几 里 得 距 离 。 求 一 个 权 值 和 最 小 的 二 叉 树 , 并 输 出 。 定义每条边的权值为两个点之间的欧几里得距离。求一个权值和最小的二叉树,并输出。
点 i 的 y 坐 标 比 j 的 y 坐 标 大 , 则 点 i 可 以 成 为 点 j 的 父 亲 。 点 i 的 y 坐标比 j 的 y 坐标大,则点i可以成为点j的父亲。 iyjyij
如 果 不 存 在 满 足 条 件 的 二 叉 树 , 输 出 − 1 。 如果不存在满足条件的二叉树,输出 −1 。 1

思路

如 果 没 有 二 叉 树 的 限 制 , 那 么 就 是 最 小 生 成 树 了 。 可 以 考 虑 网 络 流 模 型 。 如果没有二叉树的限制,那么就是最小生成树了。可以考虑网络流模型。


对 于 一 个 二 叉 树 , 一 个 根 节 点 最 多 可 以 连 接 两 个 子 节 点 。 对于一个二叉树,一个根节点最多可以连接两个子节点。

所 以 我 们 对 于 一 个 点 i , 进 行 拆 点 , 拆 成 根 节 点 i 和 子 节 点 n + i 。 所以我们对于一个点i,进行拆点,拆成根节点i和子节点n+i。 iin+i

对 于 二 叉 树 的 边 , 一 定 是 根 结 点 连 接 子 节 点 , 权 值 为 两 点 的 欧 式 距 离 。 对于二叉树的边,一定是根结点连接子节点,权值为两点的欧式距离。

根 节 点 最 多 1 个 , 子 节 点 最 多 两 个 , 所 以 我 们 根 据 上 面 所 述 : 根节点最多1个,子节点最多两个,所以我们根据上面所述: 1
a d d ( 点 i , 点 j , 流 , 费 用 ) add(点i,点j,流,费用) add(ij)

  • 源 点 连 接 子 节 点 − a d d ( s , n + i , 2 , 0 ) , 表 示 每 个 节 点 最 多 两 个 子 节 点 。 源点连接子节点-add(s, n + i, 2, 0),表示每个节点最多两个子节点。 add(s,n+i,2,0)
  • 根 结 点 连 接 汇 点 − a d d ( i , t , 1 , 0 ) , 表 示 每 个 节 点 最 多 一 个 根 节 点 。 根结点连接汇点-add(i, t, 1, 0),表示每个节点最多一个根节点。 add(i,t,1,0)
  • 根 节 点 连 接 子 节 点 − a d d ( n + i , j , 1 , D i s ( i , j ) ) , 表 示 i 可 以 成 为 j 的 根 节 点 。 根节点连接子节点-add(n+i,j,1,Dis(i,j)),表示i可以成为j的根节点。 add(n+i,j,1,Dis(i,j))ij

跑 一 遍 M C M F , 如 果 满 流 即 m a x f l o w = n − 1 , 则 该 二 叉 树 存 在 , 否 则 输 出 − 1 。 跑一遍MCMF,如果满流即maxflow=n-1,则该二叉树存在,否则输出-1。 MCMFmaxflow=n11

Code(467MS)

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;

#define endl "\n"
#define pb push_back
#define mem(a, b) memset(a , b , sizeof(a))
#define FOR(i, x, n) for(int i = x;i <= n; i++)

const int INF = 0x3f3f3f3f;
// const ll mod = 998244353;
// const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
const double R = 0.57721566490153286060651209;

const int maxn = 1005;      //点数

struct Edge {
    int from, to, cap, flow;
    double cost;

    Edge(int u, int v, int c, int f, double cc)
            : from(u), to(v), cap(c), flow(f), cost(cc) {}
};

struct MCMF {
    int n, m;
    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn];  //是否在队列中
    double d[maxn];    //bellmanford
    int p[maxn];    //上一条弧
    int a[maxn];    //可改进量
    void init(int n) {
        this->n = n;
        for (int i = 0; i <= n; ++i) G[i].clear();
        edges.clear();
    }

    void addEdge(int from, int to, int cap, double cost) {
        edges.emplace_back(Edge(from, to, cap, 0, cost));
        edges.emplace_back(Edge(to, from, 0, 0, -cost));
        m = int(edges.size());
        G[from].emplace_back(m - 2);
        G[to].emplace_back(m - 1);
    }

    bool spfa(int s, int t, int &flow, double &cost) {
        for (int i = 1; i <= n; ++i) d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0;
        inq[s] = 1;
        p[s] = 0;
        queue<int> q;
        a[s] = INF;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = 0;
            for (int i = 0; i < int(G[u].size()); ++i) {
                Edge &e = edges[G[u][i]];
                if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u], e.cap - e.flow);
                    if (!inq[e.to]) {
                        q.push(e.to);
                        inq[e.to] = 1;
                    }
                }
            }
        }
        if (d[t] == INF) return false;
        flow += a[t];
        cost += d[t] * a[t];
        for (int u = t; u != s; u = edges[p[u]].from) {
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
        }
        return true;
    }

    int MincostMaxflow(int s, int t, double &cost) {
        int flow = 0;
        cost = 0;
        while (spfa(s, t, flow, cost));
        return flow;
    }
} mcmf;

double Dis(pair<double, double> a, pair<double, double> b) {
    return sqrt(((a.first - b.first) * (a.first - b.first)) + (a.second - b.second) * (a.second - b.second));
}

bool cmp(pair<double, double> a, pair<double, double> b) {
    return a.second == b.second ? a.first < b.first : a.second > b.second;
}

void solve() {
    int n;
    scanf("%d",&n);
    mcmf.init(2 * n + 1);
    int s = 0, t = 2 * n + 1;
    pair<double, double> p[maxn];
    for(int i = 1;i <= n; i++) {
        scanf("%lf%lf",&p[i].first, &p[i].second);
        mcmf.addEdge(s, n + i, 2, 0); // 源点连接子节点
        mcmf.addEdge(i, t, 1, 0); // 根节点连接汇点
    }
    sort(p + 1, p + n + 1, cmp);
    if(p[1].second == p[2].second) { // 两个根结点?不存在的
        printf("-1\n");
        return ;
    }
    for(int i = 1;i <= n; i++) {
        for(int j = i + 1;j <= n; j++) {
            if(p[i].second == p[j].second) continue;
            mcmf.addEdge(n + i, j, 1, Dis(p[i], p[j]));
        }
    }
    double mincost;
    auto ans = mcmf.MincostMaxflow(s, t, mincost);
    if(ans == n - 1) printf("%.8lf\n",mincost);
    else printf("-1\n");
}

signed main() {
    solve();
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值