题目大意:

平面上有n个点,两两不同。现在给出二叉树的定义,要求树边一定是从上指向下,即从y坐标大的点指向小的点,并且每个结点至多有两个儿子。现在让你求给出的这些点是否能构成一棵二叉树,如果能,使二叉树的树边长度(欧几里德长度)总和最小,输出这个总和。如果不能,输出-1.答案与标准答案相差1e-6内都认为是正确的。

算法讨论:

起初是这样想的,肯定是MCMF,费用是距离,然后流量一开始我是这样搞的:从父亲向儿子连流量为2的边。但是你会发现这样有一个问题,就是如果某个结点如果真的有两个儿子的话,那么这个父亲与他的父亲之间的边的距离就会被加进去两次。表示不会解决这个问题,各种头痛。最后只得参见题解,是把一个点拆成两个点A[i] 和 B[i], S(超级源点)连向 A[i],流量为1,花费为0,B[i]全部连向T(超级汇点),流量为2,花费为0,然后扫描下,如果j满足成为i儿子的条件时,就把A[j]连向B[i],流量为1,花费为距离。注意精度问题。

至于判断是否可以是棵二叉树,我们在流完之后判断一下流量是否等于n-1就可以了。自己原来还傻子一样的去判断。

注意:

这个题如果用spfa的费用流的话,很容易写T,推荐用ZKW费用流(跑起来如飞一样,因为跑二分图特别快),但是网上的模板太不可信,找了5个,错了4个。所以自己精心翻译了一个模板。求不喷。

好像说把B[I]再次拆点,用KM就可以做了。表示自己不会KM。。学下吧。

Codes:

SPFA费用流(邻接表STL版)(TLE ON TEST 23)

 #include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std; int n;
bool flag = false; struct Edge{
int from, to, cap, flow;
double cost;
Edge(int _from=, int _to=, int _cap=, int _flow=, double _cost=):
from(_from), to(_to), cap(_cap), flow(_flow), cost(_cost) {}
}; struct Point{
int x, y;
Point(int _x = , int _y = ): x(_x), y(_y) {}
bool operator < (const Point &a) const {
if(y == a.y) return x < a.x;
return y > a.y;
}
}p[]; struct MCMF{
static const int N = + ;
static const int M = + ;
static const int oo = 0x3f3f3f3f; int n, m, s, t;
vector <Edge> edges;
vector <int> G[N];
int inque[N], pre[N], a[N];
double dis[N]; void Clear(){
for(int i = ; i <= n + ; ++ i) G[i].clear();
edges.clear();
}
void Add(int from, int to, int cp, int flw, double ct){
edges.push_back((Edge){from, to, cp, , ct});
edges.push_back((Edge){to, from, , , -ct});
int m = edges.size();
G[from].push_back(m - );
G[to].push_back(m - );
}
bool bfs(int &flw, double &ct){
for(int i = ; i <= n + ; ++ i) dis[i] = oo;
memset(inque, , sizeof inque);
dis[s] = ; a[s] = oo; inque[s] = ; pre[s] = ; queue <int> q;
q.push(s);
while(!q.empty()){
int x = q.front(); q.pop();
inque[x] = ;
for(int i = ; i < G[x].size(); ++ i){
Edge &e = edges[G[x][i]];
if(e.cap > e.flow && dis[e.to] > dis[x] + e.cost){
dis[e.to] = dis[x] + e.cost;
pre[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);
if(!inque[e.to]){
q.push(e.to);inque[e.to] = ;
}
}
}
}
if(dis[t] == (double)oo) return false;
flw += a[t];
ct += (double) dis[t] * a[t]; int now = t;
while(now != s){
edges[pre[now]].flow += a[t];
edges[pre[now]^].flow -= a[t];
now = edges[pre[now]].from;
}
return true;
}
double MinCostMaxFlow(int s, int t){
this->s = s;this->t = t;
int flw = ;
double ct = ;
while(bfs(flw, ct));
if(flw == (n / - )) flag = true;
return ct;
}
}Net; double dist(int i, int j){
return sqrt(pow(p[i].x - p[j].x, ) + pow(p[i].y - p[j].y, ));
} int main(){
scanf("%d", &n);
Net.n = n * ;
for(int i = ; i <= n; ++ i)
scanf("%d%d", &p[i].x, &p[i].y); sort(p + , p + n + );
for(int i = ; i <= n; ++ i)
Net.Add(, i, , , );
for(int i = n + ; i <= n + n; ++ i)
Net.Add(i, n + n + , , , );
for(int i = ; i <= n; ++ i){
for(int j = i + ; j <= n; ++ j){
if(p[i].y > p[j].y)
Net.Add(j, i + n, , , dist(i, j));
}
} double ans = Net.MinCostMaxFlow(, Net.n + );
if(flag) printf("%.15lf\n", ans);
else puts("-1"); return ;
}

STL

SPFA费用流(邻接表数组版)(TLE ON TEST 23)

 #include <deque>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std; int n;
bool flag = false; struct Edge{
int from, to, cap, flow;
double cost;
Edge(int _from=, int _to=, int _cap=, int _flow=, double _cost=):
from(_from), to(_to), cap(_cap), flow(_flow), cost(_cost) {}
}; struct Point{
int x, y;
Point(int _x = , int _y = ): x(_x), y(_y) {}
bool operator < (const Point &a) const {
if(y == a.y) return x < a.x;
return y > a.y;
}
}p[]; struct MCMF{
static const int N = + ;
static const int M = + ;
static const int oo = 0x3f3f3f3f; int n, m, s, t, tim, tot;
int first[N], next[M];
int u[M], v[M], cap[M], flow[M];
double cost[M];
int inque[N], pre[N], a[N];
double dis[N]; void Clear(){
tot = ;
for(int i = ; i <= n; ++ i) first[i] = -;
}
void Add(int from, int to, int cp, int flw, double ct){
u[tot] = from; v[tot] = to; cap[tot] = cp; flow[tot] = ; cost[tot] = ct;
next[tot] = first[u[tot]]; first[u[tot]] = tot; tot ++;
u[tot] = to; v[tot] = from; cap[tot] = ; flow[tot] = ; cost[tot] = -ct;
next[tot] = first[u[tot]]; first[u[tot]] = tot; tot ++;
}
bool bfs(int &flw, double &ct){
for(int i = ; i <= n + ; ++ i) dis[i] = oo; ++ tim;
dis[s] = ; a[s] = oo; inque[s] = tim; pre[s] = ;
deque <int> q;
q.push_back(s); while(!q.empty()){
int x = q.front(); q.pop_front();
inque[x] = ;
for(int i = first[x]; i != -; i = next[i]){
if(cap[i] > flow[i] && dis[v[i]] > dis[x] + cost[i]){
dis[v[i]] = dis[x] + cost[i];
pre[v[i]] = i;
a[v[i]] = min(a[x], cap[i] - flow[i]); if(inque[v[i]] != tim){
inque[v[i]] = tim;
if(!q.empty() && dis[v[i]] < dis[q.front()])
q.push_front(v[i]);
else q.push_back(v[i]);
}
}
}
}
if(dis[t] == oo) return false;
flw += a[t];
ct += (double) dis[t] * a[t]; int now = t;
while(now != s){
flow[pre[now]] += a[t];
flow[pre[now]^] -= a[t];
now = u[pre[now]];
}
return true;
}
double MinCostMaxFlow(int s, int t){
this->s = s;this->t = t;
int flw = ;
double ct = ;
while(bfs(flw, ct));
if(flw == (n / - )) flag = true;
return ct;
}
}Net; double dist(int i, int j){
return sqrt(pow(p[i].x - p[j].x, ) + pow(p[i].y - p[j].y, ));
} int main(){
scanf("%d", &n);
Net.n = n * ;
Net.Clear();
for(int i = ; i <= n; ++ i)
scanf("%d%d", &p[i].x, &p[i].y); sort(p + , p + n + );
for(int i = ; i <= n; ++ i)
Net.Add(, i, , , );
for(int i = n + ; i <= n + n; ++ i)
Net.Add(i, n + n + , , , );
for(int i = ; i <= n; ++ i){
for(int j = i + ; j <= n; ++ j){
if(p[i].y > p[j].y)
Net.Add(j, i + n, , , dist(i, j));
}
} double ans = Net.MinCostMaxFlow(, Net.n + );
if(flag) printf("%.15lf\n", ans);
else puts("-1"); return ;
}

数组版

ZKW费用流(邻接表数组版)(Accepted)

 #include <deque>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std; int n;
double ans = , cst = ;
bool flag = false; struct Edge{
int from, to, cap, flow;
double cost;
Edge(int _from=, int _to=, int _cap=, int _flow=, double _cost=):
from(_from), to(_to), cap(_cap), flow(_flow), cost(_cost) {}
}; struct Point{
int x, y;
Point(int _x = , int _y = ): x(_x), y(_y) {}
bool operator < (const Point &a) const {
if(y == a.y) return x < a.x;
return y > a.y;
}
}p[]; struct MCMF{
static const int N = + ;
static const int M = + ;
static const int oo = 0x3f3f3f3f; int n, m, s, t, tim, tot;
int first[N], next[M];
int u[M], v[M], cap[M];
double cost[M], dis[N];
bool vi[N];int cur[N]; void Clear(){
tot = ;
for(int i = ; i <= n; ++ i) first[i] = -;
}
void Add(int from, int to, int cp, int flw, double ct){
u[tot] = from; v[tot] = to; cap[tot] = cp; cost[tot] = ct;
next[tot] = first[u[tot]]; first[u[tot]] = tot; tot ++;
u[tot] = to; v[tot] = from; cap[tot] = ; cost[tot] = -ct;
next[tot] = first[u[tot]]; first[u[tot]] = tot; tot ++;
}
int aug(int x, int f){
if(x == t){
ans += (double)cst * f;
return f;
} vi[x] = true;
int tmp = f;
for(int i = first[x]; i != -; i = next[i])
if(cap[i] && !vi[v[i]] && !cost[i]){
int delta = aug(v[i], tmp < cap[i] ? tmp : cap[i]);
cap[i] -= delta;
cap[i^] += delta;
tmp -= delta;
if(tmp == ) return f;
}
return f - tmp;
}
bool modlabel(){
double tmp = (double) oo;
for(int i = ; i <= n; ++ i){
if(vi[i])
for(int j = first[i]; j != -; j = next[j])
if(cap[j] && !vi[v[j]] && cost[j] < tmp)
tmp = cost[j];
} if(tmp == (double)oo) return false;
for(int i = ; i <= n; ++ i)
if(vi[i])
for(int j = first[i]; j != -; j = next[j])
cost[j] -= tmp, cost[j^] += tmp;
cst += tmp;
return true;
}
void MinCostMaxFlow(int s, int t){
this->s = s; this->t = t;
int flw, tot=;
for(;;){
memset(vi, false, sizeof vi);
while(flw = aug(s, oo)){
tot += flw;
memset(vi, false, sizeof vi);
} if(!modlabel()) break;
}
if(tot == (n / - )) flag = true;
}
}Net; double dist(int i, int j){
return sqrt(pow(p[i].x - p[j].x, ) + pow(p[i].y - p[j].y, ));
} int main(){ scanf("%d", &n);
Net.n = n * ;
Net.Clear();
for(int i = ; i <= n; ++ i)
scanf("%d%d", &p[i].x, &p[i].y); sort(p + , p + n + );
for(int i = ; i <= n; ++ i)
Net.Add(, i, , , );
for(int i = n + ; i <= n + n; ++ i)
Net.Add(i, n + n + , , , );
for(int i = ; i <= n; ++ i){
for(int j = i + ; j <= n; ++ j){
if(p[i].y > p[j].y)
Net.Add(j, i + n, , , dist(i, j));
}
}
Net.MinCostMaxFlow(, Net.n + );
if(flag) printf("%.15lf\n", ans);
else puts("-1"); return ;
}

Accepted

恶心的提交:自己真的很渣QAQ

CF 277E Binary Tree on Plane (拆点 + 费用流) (KM也可做)的更多相关文章

  1. CF277E Binary Tree on Plane

    CF277E Binary Tree on Plane 题目大意 给定平面上的 \(n\) 个点,定义两个点之间的距离为两点欧几里得距离,求最小二叉生成树. 题解 妙啊. 难点在于二叉的限制. 注意到 ...

  2. BZOJ 1877 晨跑 拆点费用流

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1877 题目大意: Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧 ...

  3. Codefoces 277 E. Binary Tree on Plane

    题目链接:http://codeforces.com/problemset/problem/277/E 参考了这篇题解:http://blog.csdn.net/Sakai_Masato/articl ...

  4. HDU 4780 Candy Factory(拆点费用流)

    Problem Description   A new candy factory opens in pku-town. The factory import M machines to produc ...

  5. 题解【CF277E Binary Tree on Plane】

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

  6. 【拆点费用流】【HDU1853】【 Cyclic Tour】

    题意: 有N个城市,M条单向路,Tom想环游全部城市,每次至少环游2个城市,每个城市只能被环游一次.由于每条单向路都有长度,要求游遍全部城市的最小长度. // 给定一个有向图,必须用若干个环来覆盖整个 ...

  7. 洛谷P2604 网络扩容 拆点+费用流

    原题链接 这题貌似比较水吧,最简单的拆点,直接上代码了. #include <bits/stdc++.h> using namespace std; #define N 1000 #def ...

  8. BZOJ 1070 拆点 费用流

    1070: [SCOI2007]修车 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 5860  Solved: 2487[Submit][Status] ...

  9. BZOJ 1877 拆点费用流

    思路: 呃  水题不解释 行么,, //By SiriusRen #include <queue> #include <cstdio> #include <cstring ...

随机推荐

  1. php代码的一些高效写法

    用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中说 ...

  2. 常用排序算法之——选择排序(C语言+VC6.0平台)

    选择排序是另一种经典排序算法,核心思想是:在一趟找最小(大)数的过程中,先假设待排数据中的第一个数据即为最小(大)数据,然后循环将其他数据与该数据比较,每次比较时若小于该数据则让新数据成为最小(大)数 ...

  3. SVM详解

    SVM入门(一)至(三)Refresh 按:之前的文章重新汇编一下,修改了一些错误和不当的说法,一起复习,然后继续SVM之旅. (一)SVM的简介 支持向量机(Support Vector Machi ...

  4. 转:为什么需要htons(), ntohl(), ntohs(),htons() 函数

    为什么需要htons(), ntohl(), ntohs(),htons() 函数: 在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题.这是就可能用到htons(), ntohl ...

  5. Another Look at Events(再谈Events)

    转载:http://www.qtcn.org/bbs/simple/?t31383.html Another Look at Events(再谈Events) 最近在学习Qt事件处理的时候发现一篇很不 ...

  6. 设计模式(十四):Command命令模式 -- 行为型模式

    1.概述         在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来 ...

  7. 【转】编写Chrome扩展程序

    Chrome的扩展程序很多,也很容易入门,可以来简单实现一下 看看,慢慢就能实现出一个扩展程序来 每个扩展程序应用一般会包含: 一个manifest清单文件 html文件 js文件 其他文件等 可以看 ...

  8. Dijkstra优先队列优化

    Dijkstra算法的核心思想就是两步排序,一个是对于一个点而言,他的最小边要经过所有其他点最小边的测试才能确认,也就是说要在这其中找一个最大的边出来:第二个是对于每次循环而言的,每次的更新d数组都是 ...

  9. 深入理解linux网络技术内幕读书笔记(九)--中断与网络驱动程序

    Table of Contents 1 接收到帧时通知驱动程序 1.1 轮询 1.2 中断 2 中断处理程序 3 抢占功能 4 下半部函数 4.1 内核2.4版本以后的下半部函数: 引入软IRQ 5 ...

  10. CSS常用操作-导航栏

    1.垂直导航栏 index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" ...