Leapin' Lizards(最大流)

描述

传送门:HDU2732

Your platoon of wandering lizards has entered a strange room in the labyrinth you are exploring. As you are looking around for hidden treasures, one of the rookies steps on an innocent-looking stone and the room’s floor suddenly disappears! Each lizard in your platoon is left standing on a fragile-looking pillar, and a fire begins to rage below… Leave no lizard behind! Get as many lizards as possible out of the room, and report the number of casualties.
The pillars in the room are aligned as a grid, with each pillar one unit away from the pillars to its east, west, north and south. Pillars at the edge of the grid are one unit away from the edge of the room (safety). Not all pillars necessarily have a lizard. A lizard is able to leap onto any unoccupied pillar that is within d units of his current one. A lizard standing on a pillar within leaping distance of the edge of the room may always leap to safety… but there’s a catch: each pillar becomes weakened after each jump, and will soon collapse and no longer be usable by other lizards. Leaping onto a pillar does not cause it to weaken or collapse; only leaping off of it causes it to weaken and eventually collapse. Only one lizard may be on a pillar at any given time.

Input

The input file will begin with a line containing a single integer representing the number of test cases, which is at most $25$. Each test case will begin with a line containing a single positive integer $n$ representing the number of rows in the map, followed by a single non-negative integer $d$ representing the maximum leaping distance for the lizards. Two maps will follow, each as a map of characters with one row per line. The first map will contain a digit ($0-3$) in each position representing the number of jumps the pillar in that position will sustain before collapsing ($0$ means there is no pillar there). The second map will follow, with an L for every position where a lizard is on the pillar and a . for every empty pillar. There will never be a lizard on a position where there is no pillar. Each input map is guaranteed to be a rectangle of size $n \times m$, where $1 ≤ n ≤ 20$ and $1 ≤ m ≤ 20$. The leaping distance is always $1 ≤ d ≤ 3$.

Output

For each input case, print a single line containing the number of lizards that could not escape. The format should follow the samples provided below.

Sample Input

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
4
3 1
1111
1111
1111
LLLL
LLLL
LLLL
3 2
00000
01110
00000
.....
.LLL.
.....
3 1
00000
01110
00000
.....
.LLL.
.....
5 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........

Sample Output

1
2
3
4
Case #1: 2 lizards were left behind.
Case #2: no lizard was left behind.
Case #3: 3 lizards were left behind.
Case #4: 1 lizard was left behind.

思路

  • 题意:每一个人都可以最长跳$d$的距离,对于每一个柱子有一个限定条件:只能从这里跳出去多少次。问有几个人不能跳到图的外面。
  • 最大流建图,难点在于如何建图。将图的外面作为汇点,如果可以直接从柱子跳出去,就直接连到汇点。否则我们将它连到能够跳到的柱子。但是这样建图存在一个问题:如果这个柱子可以跳到旁边很多的点上,每一个边的容量都是这个限定次数的话,等于限定的次数被放大了。这样跑出的最大流显然是错误的。
  • 考虑拆点,那么就考虑拆点,将每个点拆成两个,$1→2$连一条边容量为限定的次数,如果能跳到别的点,就把$2$连到那个点的$1$,容量为$+\infty$。
  • 这样就可以限定每个点跳出去的次数,跑最大流即可。
  • PS:点的总数最大会到$800$,数组开小贡献一次RE。

代码

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <bits/stdc++.h>
using namespace std;
#define clr(a,x) memset(a, x, sizeof(a))
#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define X first
#define Y second
#define fastin ios_base::sync_with_stdio(0);cin.tie(0);
typedef long long ll;
typedef long double ld;
typedef pair<int, int> PII;
typedef vector<int> VI;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-6;

const int maxn = 1000;
const int maxm = 25;

char maze[maxm][maxm], P[maxm][maxm];

struct Edge
{
int from, to, cap, flow;
Edge(int u, int v, int c, int f): from(u), to(v), cap(c), flow(f) {}
};
struct Dinic
{
int n, m, s, t; //结点数,边数(包括反向弧),源点编号和汇点编号
vector<Edge> edges; //边表。edge[e]和edge[e^1]互为反向弧
vector<int> G[maxn]; //邻接表,G[i][j]表示节点i的第j条边在e数组中的序号
bool vis[maxn]; //BFS使用
int d[maxn]; //从起点到i的距离
int cur[maxn]; //当前弧下标
void init(int n)
{
this->n = n;
for (int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap)
{
edges.pb(Edge(from, to, cap, 0));
edges.pb(Edge(to, from, 0, 0));
m = edges.size();
G[from].pb(m - 2);
G[to].pb(m - 1);
}
bool BFS()
{
clr(vis, 0);
clr(d, 0);
queue<int> q;
q.push(s);
d[s] = 0;
vis[s] = 1;
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = 0; i < G[x].size(); i++)
{
Edge& e = edges[G[x][i]];
if (!vis[e.to] && e.cap > e.flow)
{
vis[e.to] = 1;
d[e.to] = d[x] + 1;
q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, int a)
{
if (x == t || a == 0) return a;
int flow = 0, f;
for (int& i = cur[x]; i < G[x].size(); i++)
{
//从上次考虑的弧
Edge& e = edges[G[x][i]];
if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
{
e.flow += f;
edges[G[x][i] ^ 1].flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int Maxflow(int s, int t)
{
this->s = s;
this->t = t;
int flow = 0;
while (BFS())
{
clr(cur, 0);
flow += DFS(s, INF);
}
return flow;
}
} ans;

inline int dist (int a, int b, int c, int d)
{
return abs(c - a) + abs(d - b);
}

int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
fastin
int T, n, m, d, cnt, kase = 0;
cin >> T;
while (T--)
{
cin >> n >> d;
cnt = 0;
for (int i = 0; i < n; i++)
cin >> maze[i];
for (int i = 0; i < n; i++)
cin >> P[i];
m = strlen(maze[0]);
int s = (n * m) << 1, t = s | 1;
ans.init(t + 1);
for (int i = 0; i < n; i++)
for (int j = 0 ; j < m; j++)
if (P[i][j] == 'L')
{
cnt++;
ans.AddEdge(s, (i * m + j) << 1, 1);
}
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
int cap = maze[i][j] - '0';
if (!cap)
continue;
if (i - d < 0 || i + d >= n || j - d < 0 || j + d >= m)
{
ans.AddEdge((i * m + j) << 1, t, cap);
continue;
}
ans.AddEdge((i * m + j) << 1, (i * m + j) << 1 | 1, cap);
for (int tx = -d; tx <= d; tx++)
for (int ty = -d; ty <= d; ty++)
{
int x = i + tx, y = j + ty;
if (dist(x, y, i, j) > d) continue;
ans.AddEdge((i * m + j) << 1 | 1, (x * m + y) << 1, INF);
}
}
int res = cnt - ans.Maxflow(s, t);
cout << "Case #" << ++kase << ": ";
if (res > 1)
cout << res << " lizards were left behind." << endl;
else if (res == 1)
cout << "1 lizard was left behind." << endl;
else
cout << "no lizard was left behind." << endl;
}
return 0;
}
捐助作者
0%