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 整形的长度问题
tinyint 有符号的范围是-128至127: 无符号的范围是0到255,2的8 次方-1: 存储大小为 1 字节. smallint 有符号的范围是-32768至32767: 无符号的范围是0 ...
- PHP自动添加http://头 转换网址为链接
有时候,当我们需要用户输入网址的时候,一般我们会让用户省略掉"http://",当提交完成后用代码自动再加上http://,若有需要,我们 还可将网址转换成链接的形式,类似于众多网 ...
- SQLite3简单入门及C++ API
转载请注明出处:http://www.cnblogs.com/StartoverX/p/4660487.html 项目用到SQLite3,简单记录一下. MySQL不同,SQLite3的数据库基于文件 ...
- 导入表 IMPORT_DESCRIPTOR
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null i ...
- Application.Count.ToString()和Application["count"].ToString()的区别
当属性名中包括特殊字符如 “.”或“-”就不能使用“.”操作符了.操作符只能使用[ ]操作符 为了统计网站的在线人数,我们可以在Global.asa文件中包含如下代码: <SCRIPT LANG ...
- Linux_service cloudera-scm-server start failed
see log : /var/log/cloudera-scm-server/cloudera-scm-server.log
- jdk8新特性之lambda expressions
本文分两部分: 语法简单说明 lambda的使用 注:这两部分内容均以类+注释的方式进行说明,并且内容均来自官方教程(https://docs.oracle.com/javase/tutorial/j ...
- 通过button提交表单
通过 input button 而不是input submit提交. <!DOCTYPE html> <html lang="en"> <head&g ...
- uva 10036 Problem C: Divisibility
题意:能否在一个整数序列的每相邻的两项之间添加一个加减号,使得最终结果能被一个给定整数K<=100整除. dp[i][j]表示第i个数取余k为j的布尔值. #include <cstdio ...
- Android中支持的常用距离单位
px(像素):每个px对应屏幕上的一个点.dip或dp(device independent pixels,设备独立像素):一种基于屏幕密度的抽象单位.在每英寸160点的显示器上,1dip=1px.但 ...