CF 277E Binary Tree on Plane (拆点 + 费用流) (KM也可做)
题目大意:
平面上有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也可做)的更多相关文章
- CF277E Binary Tree on Plane
CF277E Binary Tree on Plane 题目大意 给定平面上的 \(n\) 个点,定义两个点之间的距离为两点欧几里得距离,求最小二叉生成树. 题解 妙啊. 难点在于二叉的限制. 注意到 ...
- BZOJ 1877 晨跑 拆点费用流
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1877 题目大意: Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧 ...
- Codefoces 277 E. Binary Tree on Plane
题目链接:http://codeforces.com/problemset/problem/277/E 参考了这篇题解:http://blog.csdn.net/Sakai_Masato/articl ...
- HDU 4780 Candy Factory(拆点费用流)
Problem Description A new candy factory opens in pku-town. The factory import M machines to produc ...
- 题解【CF277E Binary Tree on Plane】
Description 给你平面上 \(n\) 个点 \((2 \leq n \leq 400)\),要求用这些点组成一个二叉树(每个节点的儿子节点不超过两个),定义每条边的权值为两个点之间的欧几里得 ...
- 【拆点费用流】【HDU1853】【 Cyclic Tour】
题意: 有N个城市,M条单向路,Tom想环游全部城市,每次至少环游2个城市,每个城市只能被环游一次.由于每条单向路都有长度,要求游遍全部城市的最小长度. // 给定一个有向图,必须用若干个环来覆盖整个 ...
- 洛谷P2604 网络扩容 拆点+费用流
原题链接 这题貌似比较水吧,最简单的拆点,直接上代码了. #include <bits/stdc++.h> using namespace std; #define N 1000 #def ...
- BZOJ 1070 拆点 费用流
1070: [SCOI2007]修车 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 5860 Solved: 2487[Submit][Status] ...
- BZOJ 1877 拆点费用流
思路: 呃 水题不解释 行么,, //By SiriusRen #include <queue> #include <cstdio> #include <cstring ...
随机推荐
- js 音乐
define(function(require,exports,module){ var $music = document.getElementById('music'); var $music_m ...
- Destoon后台修改公司会员资料信息的必填项限制修改
/template/default/member/edit.htm module\member\admin\template\member_edit.tpl.php module\member\adm ...
- 用VBA读取Excel表格输出到格式化的xml文件中
最近需要做一个一劳永逸的XML文档生成,给项目内部专用的,直接VBA方便了,才第一次用.现学现卖了....抽时间还是系统的学习下这方面的知识吧 输出到UTF-8编码的XML文档.并且换行符是Unix的 ...
- log4jdbc
log4jdbc http://www.blogjava.net/badqiu/archive/2010/08/20/329464.html http://blog.csdn.net/sfdev/ar ...
- Linux内核源代码解析——TCP状态转移图以及其实现
本文原创为freas_1990,转载请标明出处http://blog.csdn.net/freas_1990/article/details/10223581 TCP状态转移的原理并不高深,但是处理逻 ...
- HASH JOIN算法
哈希连接(HASH JOIN) 前文提到,嵌套循环只适合输出少量结果集.如果要返回大量结果集(比如返回100W数据),根据嵌套循环算法,被驱动表会扫描100W次,显然这是不对的.看到这里你应该明白为 ...
- crossdomain.xml跨越
crossdomain.xml是adobe搞的,为了让flash跨域访问文件. 该配置文件放于服务器端的根目录下面.来设置让哪些域名下面的swf文件能够访问我服务器上的内容. 比如:我的服务器上有个图 ...
- 大型分布式C++框架《二:大包处理过程》
本来这一篇是打算写包头在分布式平台中的具体变换过程的.其实文章已经写好了.但是想了这个应该是不能随便发表的.毕竟如果知道了一个包的具体每个字节的意义.能伪造包来攻击系统.其次来介绍一个包的具体变换过程 ...
- CentOS下MySQL无法正常启动错误
一.非正常关机/退出MySQL时报错:/var/lib/mysql/mysql.sock 1.重启机器:shutdown -h now 2.删除或重命名:rm -r /var/lib/mysql/my ...
- 双外边距浮动bug;3像素文本偏移bug;IE6以下相对定位中的绝对定位bug
http://www.cnblogs.com/star91/p/5458100.html