题目传送门

  传送门

题目大意

  给定一个费用流,每条边有一个初始流量$c_i$和单位流量费用$d_i$,增加一条边的1单位的流量需要花费$b_i$的代价而减少一条边的1单位的流量需要花费$a_i$的代价。要求最小化总费用减少量和调整次数的比值(至少调整一次)。

  根据基本套路,二分答案,移项,可以得到每条边的贡献。

  设第$i$条边的流量变化量为$m_i$,每次变化花费的平均费用为$w_i$。那么有

$\sum c_id_i - \sum (c_i + m_i)d_i + |m_i|(w_i + mid) > 0$

$\sum m_id_i+ |m_i|(w_i + mid) < 0$

  那么二分答案后等于判断是否存在一个增广环的费用和为负。(请手动参见式子脑补边权)。

  然后可以写个spfa。

Code

 /**
* bzoj
* Problem#3597
* Accepted
* Time: 464ms
* Memory: 1720k
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
typedef bool boolean; template <typename T>
void pfill(T* pst, const T* ped, T val) {
for ( ; pst != ped; *(pst++) = val);
} typedef class Edge {
public:
int ed, nx;
double w; Edge(int ed, int nx, double w) : ed(ed), nx(nx), w(w) { }
} Edge; typedef class MapManager {
public:
int* h;
vector<Edge> es; MapManager() { }
MapManager(int n) {
h = new int[(n + )];
pfill(h, h + n + , -);
}
~MapManager() {
delete[] h;
es.clear();
} void addEdge(int u, int v, double w) {
es.push_back(Edge(v, h[u], w));
h[u] = (signed) es.size() - ;
} Edge& operator [] (int p) {
return es[p];
}
} MapManager; const double dinf = 1e10;
const double eps = 1e-; typedef class Graph {
public:
int n, s;
MapManager g; int *cnt;
double *f;
boolean *vis; Graph(int n, int s) : n(n), s(s), g(n) {
cnt = new int[(n + )];
f = new double[(n + )];
vis = new boolean[(n + )];
pfill(vis, vis + n + , false);
} boolean neg_circle() {
static queue<int> que;
pfill(f, f + n + , dinf);
pfill(cnt, cnt + n + , );
f[s] = , cnt[s]++;
que.push(s);
while (!que.empty()) {
int e = que.front();
que.pop();
vis[e] = false;
for (int i = g.h[e], eu; ~i; i = g[i].nx) {
eu = g[i].ed;
double w = f[e] + g[i].w;
if (w < f[eu]) {
f[eu] = w, cnt[eu]++;
if (cnt[eu] > n)
return true;
if (!vis[eu]) {
que.push(eu);
vis[eu] = true;
}
}
}
}
return false;
}
} Graph; int n, m;
int *u, *v, *a, *b, *c, *d;
double init_ans = 0.0; inline void init() {
scanf("%d%d", &n, &m);
u = new int[(m + )];
v = new int[(m + )];
a = new int[(m + )];
b = new int[(m + )];
c = new int[(m + )];
d = new int[(m + )];
for (int i = ; i <= m; i++) {
scanf("%d%d%d%d%d%d", u + i, v + i, a + i, b + i, c + i, d + i);
init_ans += c[i] * d[i];
}
} boolean check(double mid) {
Graph graph(n + , n + );
MapManager& g = graph.g; for (int i = ; i <= m; i++) {
if (u[i] == n + ) {
g.addEdge(u[i], v[i], );
} else if (c[i]) {
g.addEdge(u[i], v[i], d[i] + b[i] + mid);
g.addEdge(v[i], u[i], -d[i] + a[i] + mid);
} else {
g.addEdge(u[i], v[i], d[i] + b[i] + mid);
}
} return graph.neg_circle();
} inline void solve() {
double l = , r = init_ans, mid;
for (int t = ; t < && l < r - eps; t++) {
mid = (l + r) / ;
if (check(mid))
l = mid;
else
r = mid;
}
printf("%.2lf\n", l);
} int main() {
init();
solve();
return ;
}

spfa

  然后再讲讲费用流做法。因为不存在负的流量,但是我们知道$c_i' \geqslant 0$,因此我们考虑将$m_i$变成$-c_i + m_i'$。

  其中$m_i'$始终非负。可以理解为这个做法就是将所有边的流量先变为0再重新调整。

  对于绝对值的部分稍微处理一下就行了。(之前那个式子脑残取了等,调了半天,sad。。。)

Code

 /**
* bzoj
* Problem#3597
* Accepted
* Time: 1168ms
* Memory: 2004k
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
typedef bool boolean; template <typename T>
void pfill(T* pst, const T* ped, T val) {
for ( ; pst != ped; *(pst++) = val);
} typedef class Edge {
public:
int ed, nx, r;
double c; Edge(int ed = , int nx = , int r = , double c = ) : ed(ed), nx(nx), r(r), c(c) { }
} Edge; typedef class MapManager {
public:
int* h;
vector<Edge> es; MapManager() { }
MapManager(int n) {
h = new int[(n + )];
pfill(h, h + n + , -);
}
~MapManager() {
delete[] h;
es.clear();
} void addEdge(int u, int v, int r, double c) {
es.push_back(Edge(v, h[u], r, c));
h[u] = (signed) es.size() - ;
} void addArc(int u, int v, int cap, double c) {
addEdge(u, v, cap, c);
addEdge(v, u, , -c);
} Edge& operator [] (int p) {
return es[p];
}
} MapManager; const signed int inf = (signed) (~0u >> );
const double dinf = 1e10;
const double eps = 1e-; template <typename T>
T __abs(T x) {
return (x < ) ? (-x) : (x);
} typedef class Network {
public:
int S, T;
MapManager g; int *le;
int *mf;
double *f;
boolean *vis; Network() { }
// be sure T is the last node
Network(int S, int T) : S(S), T(T), g(T) {
f = new double[(T + )];
le = new int[(T + )];
mf = new int[(T + )];
vis = new boolean[(T + )];
pfill(vis, vis + T, false);
}
~Network() {
delete[] f;
delete[] le;
delete[] mf;
delete[] vis;
} double spfa() {
double w;
static queue<int> que;
pfill(f, f + T + , dinf);
que.push(S);
f[S] = , le[S] = -, mf[S] = inf;
while (!que.empty()) {
int e = que.front();
que.pop();
vis[e] = false;
for (int i = g.h[e], eu; ~i; i = g[i].nx) {
if (!g[i].r)
continue;
eu = g[i].ed, w = f[e] + g[i].c;
if (w < f[eu]) {
f[eu] = w, le[eu] = i, mf[eu] = min(mf[e], g[i].r);
if (!vis[eu]) {
vis[eu] = true;
que.push(eu);
}
}
}
}
if (__abs(f[T] - dinf) < eps)
return dinf;
double rt = ;
for (int p = T, e; ~le[p]; p = g[e ^ ].ed) {
e = le[p];
g[e].r -= mf[T];
g[e ^ ].r += mf[T];
rt += mf[T] * g[e].c;
}
return rt;
} double min_cost() {
double rt = , delta;
while (__abs((delta = spfa()) - dinf) >= eps) {
rt += delta;
}
return rt;
}
} Network; // S: n + 1, T: n + 2
int n, m;
int *u, *v, *a, *b, *c, *d;
double init_ans = 0.0; inline void init() {
scanf("%d%d", &n, &m);
u = new int[(m + )];
v = new int[(m + )];
a = new int[(m + )];
b = new int[(m + )];
c = new int[(m + )];
d = new int[(m + )];
for (int i = ; i <= m; i++) {
scanf("%d%d%d%d%d%d", u + i, v + i, a + i, b + i, c + i, d + i);
init_ans += c[i] * d[i];
}
} boolean check(double mid) {
double cost = ;
Network network(n + , n + );
MapManager& g = network.g;
for (int i = ; i <= m; i++) {
if (u[i] == n + ) {
g.addArc(u[i], v[i], c[i], );
} else {
g.addArc(u[i], v[i], c[i], -(a[i] + mid) + d[i]);
g.addArc(u[i], v[i], inf, b[i] + mid + d[i]);
// cost += c[i] * (-d[i] + a[i] + mid);
cost += c[i] * (-d[i] + a[i] + mid);
}
}
cost += network.min_cost();
return cost < ;
} inline void solve() {
double l = , r = init_ans, mid;
for (int t = ; t < && l < r - eps; t++) {
mid = (l + r) / ;
if (check(mid))
l = mid;
else
r = mid;
}
printf("%.2lf\n", l);
} int main() {
init();
solve();
return ;
}

bzoj 3597 [Scoi2014] 方伯伯运椰子 - 费用流 - 二分答案的更多相关文章

  1. bzoj 3597: [Scoi2014]方伯伯运椰子 0/1分数规划

    3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 144  Solved: 78[Submit][Status ...

  2. bzoj 3597: [Scoi2014]方伯伯运椰子 [01分数规划 消圈定理 spfa负环]

    3597: [Scoi2014]方伯伯运椰子 题意: from mhy12345 给你一个满流网络,对于每一条边,压缩容量1 需要费用ai,扩展容量1 需要bi, 当前容量上限ci,每单位通过该边花费 ...

  3. bzoj 3597: [Scoi2014]方伯伯运椰子

    Description Input 第一行包含二个整数N,M 接下来M行代表M条边,表示这个交通网络 每行六个整数,表示Ui,Vi,Ai,Bi,Ci,Di 接下来一行包含一条边,表示连接起点的边 Ou ...

  4. 3597: [Scoi2014]方伯伯运椰子[分数规划]

    3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 404  Solved: 249 [Submit][Sta ...

  5. BZOJ 3597 SCOI2014 方伯伯送椰子 网络流分析+SPFA

    原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3597 Description 四川的方伯伯为了致富,决定引进海南的椰子树.方伯伯的椰子园十 ...

  6. bzoj3597[Scoi2014]方伯伯运椰子 01分数规划+spfa判负环

    3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 594  Solved: 360[Submit][Statu ...

  7. BZOJ3597 SCOI2014方伯伯运椰子(分数规划+spfa)

    即在总流量不变的情况下调整每条边的流量.显然先二分答案变为求最小费用.容易想到直接流量清空跑费用流,但复杂度略有些高. 首先需要知道(不知道也行?)一种平时基本不用的求最小费用流的算法——消圈法.算法 ...

  8. Bzoj3597: [Scoi2014]方伯伯运椰子

    题面 传送门 Sol 消圈定理:如果一个费用流网络的残量网络有负环,那么这个费用流不优 于是这个题就可以建出残量网络,然后分数规划跑负环了 # include <bits/stdc++.h> ...

  9. [SCOI2014]方伯伯运椰子

    嘟嘟嘟 01分数规划思维题. 题中要求交通总量不减少,那么如果总量增加的话,总费用就会增加,所以一定不是更优的解.那么总量守恒. 这是不是就想到了网络流?对于每一个节点流入量等于流出量.然后就是很有思 ...

随机推荐

  1. 《SQL Server性能调优实战》知识点汇总

    2.4数据库结构的设计 好的性能出自好的设计 尽可能添加数据完整约束(非空约束.默认值约束.CHECK约束.唯一约束.外键约束)等,这些约束的添加将有助于数据库关系引擎分析执行计划. 尽可能小的字段类 ...

  2. ARE 212 - Problem Set 5

    ARE 212 - Problem Set 5Due May 1stPart I: Theory (Optional)1. Show that the parameter estimates for ...

  3. 第一编,漫漫长征路,第一天学习python

    安装之后,出现 api-ms-win-crt-runtimel1-1-0.dll缺失   还在解决中 重装系统后,安装成功 python的种类: javapython cpython pypy

  4. 如何把一个vue组件改为ionic/angular组件

    同是mvvm框架,他们之间是很相似的,如何你已经熟悉其中的一个,那么另一个也就基本上也就会的差不多了. 一.动态属性.值.事件绑定 vue中使用v-bind:或者之间分号:进行绑定 ng中左括号[]进 ...

  5. Python 笔记 v1 - json和字符串互转

    字符串转为json对象并取值 >>> 使用了Json包中的loads()方法和dumps()方法 string =" { "status": " ...

  6. Python004-数据处理示例:以某个数据(字段)为基准从数据中获取不同的字段行数

    数据源样式如下所示: 需求: 读取文本,以第一列为基准参考系,每个基准仅输出满足需要条数的数据:不满足,全部输出. 比如,基准为 6236683970000018780,输出条数要求为 5.若文本中含 ...

  7. sqlplus用户登录

    1.运行SQLPLUS工具 C:\Users\wd-pc>sqlplus 2.直接进入SQLPLUS命令提示符 C:\Users\wd-pc>sqlplus /nolog 3.以OS身份连 ...

  8. 音乐类产品——“网易云音乐”app交互原型模板(免费使用)

    网易云音乐虽是一款音乐app,但有人说它也是社交界的一股清流以及一匹黑马.音乐带给人的感染,激发着很多人在这里表达着他们的情绪和心声.网易云音乐上的真实用户点评,不仅被印在地铁的广告牌上,还在朋友圈频 ...

  9. JavaScript 判断当前设备是否是移动端还是PC

    if(navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i)){ alert('移动端')}else { alert('PC端') }

  10. Oracle中对XMLType的简单操作(extract、extractvalue...)

    Oracle中对XMLType的简单操作(extract.extractvalue...)    1.下面先创建一个名未test.xml的配置文件. <?xml version="1. ...