「网络流24题」火星探险问题-题解

题目传送门: 「Luogu P3356」火星探险问题

题目大意

有$n$辆车,$p\times q$的网格
为0可以通过,1有障碍不能通过,2为岩石可以采集
从(1, 1)开始到最右下角,只能向右或向下

求出使到达终点的车最多,而且采集的岩石最多的移动方案

题解

将每个位置拆成入点和出点

  1. 如果这个位置是$0$或$2$, 则从 入点 向 出点 接一条 容量为$inf$, 费用为$0$ 的边
  2. 如果这个位置是$2$, 则从 入点 向 出点 接一条 容量为$1$, 费用为$1$ 的边
  3. 如果这个位置$u$的右边$v$是不是$1$, 则从 $u$的出点 向 $v$的入点 接一条 容量为$inf$, 费用为$0$ 的边
  4. 如果这个位置$u$的下边$v$是不是$1$, 则从 $u$的出点 向 $v$的入点 接一条 容量为$inf$, 费用为$0$ 的边

跑最大费用最大流,最大流数即到达终点最多的车数
输出方案使用dfs,在流量网络中搜索输出路径

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <bits/stdc++.h>
using namespace std;

inline int read() {
int x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - 48; ch = getchar();}
return x * f;
}

const int maxn = 4010;
const int inf = 0x3f3f3f3f;
const int ninf = 0xc0c0c0c0;

int n, m, s, t, ansflow;
int vis[maxn], d[maxn], pre[maxn], a[maxn];
long long anscost;

struct Edge {
int from, to, cap, flow, cost;
Edge(int u, int v, int c, int f, int w): from(u), to(v), cap(c), flow(f), cost(w){}
};
vector<Edge> edges;
vector<int> G[maxn];
void add(int u, int v, int c, int w) {
edges.push_back(Edge(u, v, c, 0, w));
edges.push_back(Edge(v, u, 0, 0,-w));
int mm = edges.size();
G[u].push_back(mm - 2);
G[v].push_back(mm - 1);
}

bool BellmanFord(int& flow, long long& cost) {
memset(d, 0xc0, sizeof(d));
memset(vis, 0, sizeof(vis));
d[s] = 0; vis[s] = 1; pre[s] = 0; a[s] = inf;
queue<int> Q;
Q.push(s);
while (!Q.empty()) {
int x = Q.front(); Q.pop();
vis[x] = 0;
for (int i = 0; i < G[x].size(); ++i) {
Edge& e = edges[G[x][i]];
if (e.cap > e.flow && d[e.to] < d[x] + e.cost) {
d[e.to] = d[x] + e.cost;
pre[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);
if (!vis[e.to]) {
Q.push(e.to);
vis[e.to] = 1;
}
}
}
}
if (d[t] == ninf) return false;
flow += a[t];
cost += (long long)d[t] * (long long)a[t];
for (int u = t; u != s; u = edges[pre[u]].from) {
edges[pre[u]].flow += a[t];
edges[pre[u] ^ 1].flow -= a[t];
}
return true;
}

int MaxCostMaxFlow(long long& cost) {
int flow = 0; cost = 0;
while (BellmanFord(flow, cost));
return flow;
}

int p, q;
int in[40][40];

int point(int x, int y) {
return (x - 1) * p + y;
}

void dfs(int x, int y, int u, int id) {
for (int i = 0; i < G[u].size(); ++i) {
Edge& e = edges[G[u][i]];
Edge& ne = edges[G[u][i] ^ 1];
if (e.to == s || e.to == t || e.to == u - n) continue;
if (!e.flow) continue;
e.flow--;
if (e.to > n) {
dfs(x, y, e.to, id);
return;
}
int nx, ny, dir;
if (e.to == point(x, y) + 1) {
nx = x; ny = y + 1;
dir = 1;
} else {
nx = x + 1; ny = y;
dir = 0;
}
printf("%d %d\n", id, dir);
dfs(nx, ny, e.to + n, id);
return;
}
}


int main() {
int c = read(); p = read(); q = read();
n = p * q; s = 0; t = 2 * n + 1;
for (int i = 1; i <= q; ++i) {
for (int j = 1; j <= p; ++j) {
in[i][j] = read();
if (in[i][j] == 0) add(point(i, j), point(i, j) + n, inf, 0);
if (in[i][j] == 2) {
add(point(i, j), point(i, j) + n, inf, 0);
add(point(i, j), point(i, j) + n, 1, 1);
}
}
}
if (in[1][1] != 1) add(s, 1, c, 0);
for (int i = 1; i <= q; ++i) {
for (int j = 1; j <= p; ++j) {
if (in[i][j] == 1) continue;
if (in[i][j + 1] != 1 && j + 1 <= p) add(point(i, j) + n, point(i, j + 1), inf, 0);
if (in[i + 1][j] != 1 && i + 1 <= q) add(point(i, j) + n, point(i + 1, j), inf, 0);
}
}
if (in[q][p] != 1) add(point(q, p) + n, t, c, 0);
ansflow = MaxCostMaxFlow(anscost);
// printf("%d %d\n", ansflow, anscost);
for (int i = 1; i <= ansflow; ++i) {
dfs(1, 1, 1, i);
}
return 0;
}

「网络流24题」火星探险问题-题解

https://blog.tonycrane.cc/p/e7256d1.html

作者

TonyCrane

发布于

2020-04-20

更新于

2020-05-05

许可协议