Skip to content

Commit

Permalink
最小费用流
Browse files Browse the repository at this point in the history
  • Loading branch information
yogykwan committed May 5, 2017
1 parent f453e65 commit 13dc034
Show file tree
Hide file tree
Showing 6 changed files with 598 additions and 25 deletions.
49 changes: 24 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# 1 前言

项目为[《挑战程序设计竞赛(第2版)》](http://www.ituring.com.cn/book/1044)习题册,配合书籍或笔记,系统学习算法。
项目为[《挑战程序设计竞赛(第2版)》](http://www.ituring.com.cn/book/1044)习题册攻略,已完结。可配合书籍或笔记,系统学习算法。

* 题量:约200道,代码注释内含详解。
* 难度:总体高于Leetcode,部分接近ACM。
* 题解:代码均AC,题解个人向;Bug或优化请建Issue或Pull Request。
* 计划:持续更新,保证2017年5月初完成。

# 1.1 题库来源
- Google Code Jam([GCJ](https://code.google.com/codejam)
Expand All @@ -22,29 +21,29 @@
# 1.3 题库目录

* 初级:
[穷竭搜索](#21-穷竭搜索)
[贪心法](#22-贪心法)
[动态规划](#23-动态规划)
[数据结构](#24-数据结构)
[图论](#25-图论)
[数论](#26-数论)
[穷竭搜索](#21-穷竭搜索)
[贪心法](#22-贪心法)
[动态规划](#23-动态规划)
[数据结构](#24-数据结构)
[图论](#25-图论)
[数论](#26-数论)

* 中级:
[二分搜索](#31-二分搜索)
[常用技巧](#32-常用技巧)
[数据结构(二)](#33-数据结构二)
[动态规划(二)](#34-动态规划二)
[二分搜索](#31-二分搜索)
[常用技巧](#32-常用技巧)
[数据结构(二)](#33-数据结构二)
[动态规划(二)](#34-动态规划二)
[网络流](#35-网络流)
[计算几何](#36-计算几何)
[计算几何](#36-计算几何)

* 高级:
[数论(二)](#41-数论二)
[博弈论](#42-博弈论)
[图论(二)](#43-图论二)
[常用技巧(二)](#44-常用技巧二)
[智慧搜索](#45-智慧搜索)
[分治](#46-分治)
[字符串](#47-字符串)
[数论(二)](#41-数论二)
[博弈论](#42-博弈论)
[图论(二)](#43-图论二)
[常用技巧(二)](#44-常用技巧二)
[智慧搜索](#45-智慧搜索)
[分治](#46-分治)
[字符串](#47-字符串)

# 2 初级算法

Expand Down Expand Up @@ -232,11 +231,11 @@
- [x] [AOJ 2251: Merry Christmas](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2251)

- 最小费用流
- [ ] [POJ 3068: ](http://poj.org/problem?id=3068)
- [ ] [POJ 2195: ](http://poj.org/problem?id=2195)
- [ ] [POJ 3422: ](http://poj.org/problem?id=3422)
- [ ] [AOJ 2266: ](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2266)
- [ ] [AOJ 2230: ](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2230)
- [x] [POJ 3068: "Shortest" pair of paths](http://poj.org/problem?id=3068)
- [x] [POJ 2195: Going Home](http://poj.org/problem?id=2195)
- [x] [POJ 3422: Kaka's Matrix Travels](http://poj.org/problem?id=3422)
- [x] [AOJ 2266: Cache Strategy](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2266)
- [x] [AOJ 2230: How to Create a Good Game](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2230)

# 3.6 计算几何
- 极限
Expand Down
136 changes: 136 additions & 0 deletions src/aoj2230.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* AOJ 2230: How to Create a Good Game
* 题意:给出一个带权有向无环图,在保证起点到终点的最长路不变前提下,可增加一些边的权。求最大能增加的边权总和。
* 类型:最小费
* 算法:输入的边两端连接容量INF花费为负边权的边,Dijkstra求最长路。从终点到起点连接容量无穷、花费为最长路的边。构造源s汇t,s与入度大于出度的点连容量度差花费的本容量1花费0的边,出度大的向t连类似的边。求得的最小费为所有圈的正权和,再加上负权和(输入边权和的相反数)即为答案。
*/

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> pii;

const int INF = 0x3f3f3f3f;

int in[110], out[110];

struct Edge {
Edge() {}

Edge(int _v, int _cap, int _cost, int _rev) : v(_v), cap(_cap), cost(_cost), rev(_rev) {}

int v, cap, cost, rev;
};

vector<Edge> e[110];
int n;
int h[110], d[110];
int pv[110], pe[110];
priority_queue<pii, vector<pii>, greater<pii> > pq;

void AddEdge(int u, int v, int cap, int cost) {
e[u].push_back(Edge(v, cap, cost, e[v].size()));
e[v].push_back(Edge(u, 0, -cost, e[u].size() - 1));
}

int MinCostFlow(int s, int t, int f) {
int ans = 0;
memset(h, 0, sizeof(h));
while (f > 0) {
memset(d, 0x3f, sizeof(d));
d[s] = 0;
pq.push(make_pair(0, s));
while (!pq.empty()) {
int u = pq.top().second;
int pre = pq.top().first;
pq.pop();
if (d[u] < pre) continue;
for (int i = 0; i < e[u].size(); ++i) {
Edge &te = e[u][i];
int v = te.v;
if (te.cap > 0 && d[v] > d[u] + h[u] - h[v] + te.cost) {
d[v] = d[u] + h[u] - h[v] + te.cost;
pv[v] = u;
pe[v] = i;
pq.push(make_pair(d[v], v));
}
}
}

if (d[t] == INF) {
return -1;
}
for (int i = 0; i < n; ++i) {
h[i] += d[i];
}

int cur = f;
for (int i = t; i != s; i = pv[i]) {
cur = min(cur, e[pv[i]][pe[i]].cap);
}
ans += cur * h[t];
f -= cur;
for (int i = t; i != s; i = pv[i]) {
Edge &te = e[pv[i]][pe[i]];
te.cap -= cur;
e[i][te.rev].cap += cur;
}
}
return ans;
}

int main() {
int m;
scanf("%d%d", &n, &m);
int s = n, t = n + 1;
int sum = 0;
for (int i = 0; i < m; ++i) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
AddEdge(u, v, INF, -c);
sum += c;
++in[v];
++out[u];
}
int degree = 0;
for (int i = 0; i < n; ++i) {
if (in[i] > out[i]) {
degree += in[i] - out[i];
}
}

memset(d, 0x3f, sizeof(d));
d[0] = 0;
pq.push(make_pair(0, 0));
while (!pq.empty()) {
int u = pq.top().second;
int pre = pq.top().first;
pq.pop();
if (d[u] < pre) continue;
for (int i = 0; i < e[u].size(); ++i) {
Edge &te = e[u][i];
int v = te.v;
if (te.cap > 0 && d[v] > d[u] + te.cost) {
d[v] = d[u] + te.cost;
pq.push(make_pair(d[v], v));
}
}
}

AddEdge(n - 1, 0, INF, -d[n - 1]);
for (int i = 0; i < n; ++i) {
if (in[i] > out[i]) {
AddEdge(s, i, in[i] - out[i], 0);
} else {
AddEdge(i, t, out[i] - in[i], 0);
}
}
n += 2;
int ans = MinCostFlow(s, t, degree) - sum;
printf("%d\n", ans);
return 0;
}
109 changes: 109 additions & 0 deletions src/aoj2266.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* AOJ 2266: Cache Strategy
* 题意:有n个重量不同的球和m个框,再给出一个可重复的放球序列。依照序列顺序将此刻不在框内的球放入,若选择放入的框已有球,则需花费新球数量的重量来替换。求最小总花费。
* 类型:最小费
* 算法:将输入序列相邻相等的驱去重后,如每次都替换,则总花费为新序列的重量和。在序列中,两个相同的球之间的左闭右开序列区间如果放在同一框内,则总花费可减去该球重量。建图时可将上述区间的起点和终点建容量1花费负重量的边,再用容量为INF花费0的边将整个图连起来。求最小费用流即为可以节约的最大值的相反数,加上预处理的原始总重量即为答案。因为图有负环,所以求最短增广路不能用Dijkstra,改用Bellman-Ford。
*/

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> pii;

const int INF = 0x3f3f3f3f;

int a[10010], w[10010];
int pre[10010];

struct Edge {
Edge() {}

Edge(int _v, int _cap, int _cost, int _rev) : v(_v), cap(_cap), cost(_cost), rev(_rev) {}

int v, cap, cost, rev;
};

vector<Edge> e[10010];
int n;
int d[10010];
int pv[10010], pe[10010];

void AddEdge(int u, int v, int cap, int cost) {
e[u].push_back(Edge(v, cap, cost, e[v].size()));
e[v].push_back(Edge(u, 0, -cost, e[u].size() - 1));
}

int MinCostFlow(int s, int t, int f) {
int ans = 0;
while (f > 0) {
memset(d, 0x3f, sizeof(d));
d[s] = 0;
bool update = true;
while (update) {
update = false;
for (int i = 0; i < n; ++i) {
if (d[i] == INF) continue;
for (int j = 0; j < e[i].size(); ++j) {
Edge &te = e[i][j];
if (te.cap > 0 && d[te.v] > d[i] + te.cost) {
d[te.v] = d[i] + te.cost;
pv[te.v] = i;
pe[te.v] = j;
update = true;
}
}
}
}

if (d[t] == INF) {
return -1;
}

int cur = f;
for (int i = t; i != s; i = pv[i]) {
cur = min(cur, e[pv[i]][pe[i]].cap);
}
ans += cur * d[t];
f -= cur;
for (int i = t; i != s; i = pv[i]) {
Edge &te = e[pv[i]][pe[i]];
te.cap -= cur;
e[i][te.rev].cap += cur;
}
}
return ans;
}

int main() {
int M, N, K, ans = 0;
scanf("%d%d%d", &M, &N, &K);
for (int i = 0; i < N; ++i) {
scanf("%d", &w[i]);
}
for (int i = 0; i < K; ++i) {
scanf("%d", &a[i]);
--a[i];
}
K = unique(a, a + K) - a;

memset(pre, -1, sizeof(pre));
for (int i = 0; i < K; ++i) {
ans += w[a[i]];
if (pre[a[i]] != -1) {
AddEdge(pre[a[i]], i - 1, 1, -w[a[i]]);
}
pre[a[i]] = i;
}
for (int i = 1; i < K; ++i) {
AddEdge(i - 1, i, M - 1, 0);
}
n = K;
ans += MinCostFlow(0, n - 1, M - 1);
printf("%d\n", ans);

return 0;
}
Loading

0 comments on commit 13dc034

Please sign in to comment.