P2472 [SCOI2007]蜥蜴
分析
这次来到了一个正儿八经的省选题,紫题难度的,高中那会根本不敢想......
说老实话,如果不是他被放在网络流题单里面,我也不会想到用网络流来做,就离谱
突然发现,这种题目好像有一种共通的性质符合网络流:
- 都有一个东西需要从一个地方到另一个地方(即 流量,这题中是蜥蜴)
- 每次移动会对所在的地方造成一种损耗,损耗达到一定限度就不再有效(即 边的容量,在这题中是柱子的高度)
- 有起点和终点(建立源点和汇点)
既然这样,我们就可以跑一边最大流看看
每个柱子有高度,我们可以使用拆点法拆成两个点,中间所连边的边权为其高度。
柱子和柱子之间暴力连边,好在数据范围很小,并不会T掉。只要柱子高度足够,跳起来并没有啥限制,所以边权设为 INF。
每个柱子看下能不能跳到外面,这里也可以暴力连边,将他们连到汇点,边权设置为 INF。
从源点向每只蜥蜴在的地方连一条边,边权设为 1。(只有一只蜥蜴)
建好图之后,跑一边最大流就完事了。
对了,题目是让求跑不掉的蜥蜴的最小值,所以答案是蜥蜴总数减去最大流的所得值。
代码
#include<bits/stdc++.h>
using namespace std;
const int INF = 1 << 30, N = 1010;
struct Edge {
int from, to, flow;
Edge(int u, int v, int f):from(u), to(v), flow(f) {}
};
int n, m, R, s, t;
vector<Edge> edges;
vector<int> G[N];
void addEdge(int from, int to, int flow) {
edges.push_back((Edge){from, to, flow});
edges.push_back((Edge){to, from, 0 });
G[from].push_back(edges.size() - 2);
G[ to ].push_back(edges.size() - 1);
}
int d[N];
queue<int>q;
bool bfs() {
memset(d, 0, sizeof(d));
while (!q.empty()) q.pop();
q.push(s), d[s] = 1;
while (!q.empty()) {
int x = q.front(); q.pop();
if (x == t) return true;
for (int i = 0; i < G[x].size(); ++i) {
Edge &e = edges[G[x][i]];
int to = e.to;
if (!d[to] && e.flow)
q.push(to), d[to] = d[x] + 1;
}
}
return false;
}
int Dinic (int x, int flow) {
if(x == t) return flow;
int rest = flow;
for (int i = 0; i < G[x].size(); ++i) {
Edge &e = edges[G[x][i]], &fe = edges[G[x][i]^1];
int to = e.to;
if (e.flow && d[to] == d[x] + 1) {
int k = Dinic(to, min(rest, e.flow));
if (!k) d[to] = 0;
e.flow -= k, fe.flow += k;
rest -= k;
if (rest == 0) break;
}
}
if (flow == rest) d[x] = 0;
return flow - rest;
}
//
inline int getNum(int x, int y,int id) {
return 2 * (m*(x-1)+y) - (2-id);
}
inline int dis2(int x, int y, int i, int j) {
return (x - i) * (x - i) + (y - j) * (y - j);
}
int main()
{
//input
scanf("%d%d%d", &n, &m, &R);
s = 0, t = 2 * n * m + 1;
char ms[30];
for (int i = 1; i <= n; ++i){
scanf("%s", ms + 1);
for (int j = 1; j <= m; ++j)
if (ms[j] != '0')
addEdge(getNum(i, j, 1), getNum(i, j, 2), ms[j] - '0');
}
int animals = 0;
for (int i = 1; i <= n; ++i){
scanf("%s", ms + 1);
for (int j = 1; j <= m; ++j)
if (ms[j] == 'L') {
++animals;
addEdge(s, getNum(i, j, 1), 1);
}
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
for (int x = 1; x <= n; ++x)
for (int y = 1; y <= m; ++y)
if (dis2(x, y, i, j) <= R * R)
addEdge(getNum(i, j, 2), getNum(x, y, 1), INF);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
bool flag = false;
for(int k = 0; k <= m + 1 && !flag; ++k)
if (dis2(0, k, i, j) <= R * R || dis2(n + 1, k, i, j) <= R * R)
addEdge(getNum(i, j, 2), t, INF), flag = true;
for (int k = 1; k <= n && !flag; ++k)
if (dis2(k, 0, i, j) <= R * R || dis2(k, m + 1, i, j) <= R * R)
addEdge(getNum(i, j, 2), t, INF), flag = true;
}
//solve
int maxflow = 0;
int flow;
while (bfs())
while (flow = Dinic(s, INF)) maxflow += flow;
//output
printf("%d\n", animals - maxflow);
return 0;
}