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 ...
随机推荐
- mysql 索引管理原则
最近在学习mysql的索引优化,结合着我们网盟系统的一些业务,翻阅一些资料,整理出如下的一些想法: 1.索引建立的原则一:最左前缀匹配原则 ,非常重要的原则,mysql会一直向右匹配直到遇到范围查询( ...
- 前端MVC学习笔记(一)——MVC概要与angular概要、模板与数据绑定
一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.AngularJS是框架而jQuery则是库. 1.2. ...
- 发现一个时隐时现的bug!
在awk里可以这样使用正则: #截取 a.cn?fr= 中的1211 -]+/) > ) { fr = substr(url,RSTRART + , RLENGTH - ) } #截取 a.cn ...
- Verilog HDL中阻塞语句和非阻塞语句的区别
在Verilog中有两种类型的赋值语句:阻塞赋值语句(“=”)和非阻塞赋值语句(“<=”).正确地使用这两种赋值语句对于Verilog的设计和仿真非常重要. Verilog语言中讲的阻塞赋值与非 ...
- HTML5 + CSS3 + JavaScript
http://www.programmer.com.cn/14761/#more-14761 文/李晶 随着互联网产业的爆炸式增长,与之伴生的Web前端技术也在历经洗礼和蜕变.尤其是近几年随着移动终端 ...
- 理解 break, continue, return 和 exit
你们知道 “break”, “continue”, “return” 和 “exit”的作用吗? 它们是功能强大的语言结构体.下面通过一个测试函数来说明它们之间的不同. 1 2 3 4 5 6 7 8 ...
- css属性之!important
提升指定样式规则的应用优先权. IE6及以下浏览器有个比较显式的支持问题存在,!important在同一条规则集里不生效.请看下述代码: div { color: #f00 !important; c ...
- 设计模式 ( 十六 ): Mediator中介者模式 -- 行为型
1.概述 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责,即将行为分布到各个对象中. 对于一个模块或者系统,可能由很多对象构成,而且这些对象 ...
- 【8】JAVA---地址App小软件(AddrDaoFile .class)(数据层)
实现数据进行文件的存储和读写. 本软件也就到此结束了. 没多少可以讲的. 因为这个小软件也就8个类,主要学习的也就是一个分层思想的简单应用. package cn.hncu.addr.dao; imp ...
- cf509E Pretty Song
E. Pretty Song time limit per test 1 second memory limit per test 256 megabytes input standard input ...