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 ...
随机推荐
- php stripslashes() addslashes() 解析
stripslashes() 函数删除由 addslashes() 函数添加的反斜杠. 实例: <?php $str = "Is your name O\'reilly?"; ...
- c++ 输出时间
[root@GZMJ_Logic_2 ]# cat lys.cpp #include "stdio.h" #include <iostream> #include< ...
- 转:Red Hat JBoss团队发布WildFly 8,全面支持Java EE 7并包含全新的嵌入式Web服务器
原文来自于:http://www.infoq.com/cn/news/2014/02/wildfly8-launch Red Hat的JBoss部门今天宣布WildFly 8正式发布.其前身是JBos ...
- MFC的GUI窗口使用Console输出函数printf
在GUI程序中使用printf函数: #include <io.h> #include <fcntl.h> void InitConsole() { ; FILE* fp; A ...
- ASP.NET MVC – 关于Action返回结果类型的事儿(上)
原文:ASP.NET MVC – 关于Action返回结果类型的事儿(上) 本文转自:博客园-文超的技术博客 一. ASP.NET MVC 1.0 Result 几何? Action的 ...
- vector,list和deque区别
stl提供了三个最基本的容器:vector,list,deque. vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由 ...
- 用QtWebKit开发简单的浏览器
用QtWebKit开发简单的浏览器 1.代码实现 工程目录结构如下: AddressBar类包含了地址栏和按钮两个控件,将地址栏回车和按钮点击信号与goToSite()槽连接. 当回车和点击事件发生时 ...
- 设计模式(八):Bridge桥接模式 -- 结构型模式
1. 概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度 ...
- 【转】你应该知道的 10 个 VirtualBox 技巧与高级特性
原文网址:http://www.oschina.net/translate/10-virtualbox-tricks-and-advanced-features-you-should-know-abo ...
- cf500A New Year Transportation
A. New Year Transportation time limit per test 2 seconds memory limit per test 256 megabytes input s ...