Description
在一个 \(r\) 行 \(c\) 列的网格地图中有一些高度不同的石柱, 一些石柱上站着一些蜥蜴, 你的任务是让尽量多的蜥蜴逃到边界外. 每行每列中相邻石柱的距离为 \(1\), 蜥蜴的跳跃距离是 \(d\), 即蜥蜴可以跳到平面距离不超过 \(d\) 的任何一个石柱上. 石柱都不稳定, 每次当蜥蜴跳跃时, 所离开的石柱高度减 \(1\) (如果仍然落在地图内部, 则到达的石柱高度不变), 如果该石柱原来高度为 \(1\), 则蜥蜴离开后消失. 以后其他蜥蜴不能落脚. 任何时刻不能有两只蜥蜴在同一个石柱上.
Input
输入第一行为三个整数 \(r, c, d\), 即地图的规模与最大跳跃距离.
以下 \(r\) 行为石竹的初始状态,\(0\) 表示没有石柱,\(1-3\) 表示石柱的初始高度.
以下 \(r\) 行为蜥蜴位置,"L"
表示蜥蜴,"."
表示没有蜥蜴.
Output
输出仅一行, 包含一个整数, 即无法逃离的蜥蜴总数的最小值.
Sample Input
5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........
Sample Output
1
Data Range
100% 的数据满足:\(1 \leq r, c \leq 20, 1 \leq d \leq 4\)
Explanation
此题用网络流过掉, 但是还是拆点, 而且是经典的把一个几何点拆成两个拓扑点. 比如一个 点负责流入, 另一个流出; 流入和流出之间一定有一个限制流量即允许从该处跳出的青蛙数量. 进一步地, 可以从一个流出连到另一个流入, 即青蛙的移动法则.
大概瞎搞一发 Dinic 可以水过......
Source Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cstring>
#define rep(_var,_begin,_end) for (int _var=_begin;_var<=_end;_var++)
#define sqr(__x) ((__x)*(__x))
using namespace std;
typedef long long lli;
const int maxN = 100, maxn = 10100, maxm = 500100;
const int infinit = 0x0f7f7f7f;
class Dinic
{
public:
struct edge
{
int u, v, flow;
edge *next, *rev;
};
int n, s, t, ecnt;
edge *edges[maxn], epool[maxm];
int level[maxn];
void add_edge(int u, int v, int flow)
{
// printf("%d %d %d\n", u, v, flow);
edge *p = &epool[++ecnt],
*q = &epool[++ecnt];
p->u = u; p->v = v; p->flow = flow;
p->next = edges[u]; edges[u] = p; p->rev = q;
q->u = v; q->v = u; q->flow = 0;
q->next = edges[v]; edges[v] = q; q->rev = p;
return ;
}
bool make_level(void)
{
memset(level, 0, sizeof(level));
queue<int> que;
que.push(s);
level[s] = 1;
while (!que.empty()) {
int p = que.front();
que.pop();
for (edge *ep = edges[p]; ep; ep = ep->next)
if (ep->flow && !level[ep->v]) {
level[ep->v] = level[p] + 1;
que.push(ep->v);
}
}
if (level[t] > 0)
return true;
return false;
}
int dfs(int p, int mn)
{
if (p == t)
return mn;
int sum = 0, tmp;
for (edge *ep = edges[p]; ep && sum < mn; ep = ep->next)
if (ep->flow && level[ep->v] == level[p] + 1) {
tmp = dfs(ep->v, min(mn - sum, ep->flow));
if (tmp > 0) {
sum += tmp;
ep->flow -= tmp;
ep->rev->flow += tmp;
}
}
return sum;
}
int eval(void)
{
int sum = 0, tmp;
while (make_level()) {
tmp = dfs(s, infinit);
if (tmp <= 0)
break;
sum += tmp;
}
return sum;
}
} graph;
int read_number(void) { char ch = '\0';
while (ch < '0' || ch > '9') ch = getchar();
return ch - '0'; }
bool read_position(void) { char ch = '\0';
while (ch != '.' && ch != 'L') ch = getchar();
return ch == 'L'; }
int r, c, d;
int arr[maxN][maxN];
#define get_pos(__y,__x) (((__y)-1)*c+(__x))
bool judge_node_pair(int x1, int y1, int x2, int y2)
{
// if (x1 < 1 || x1 > c || x2 < 1 || x2 > c) return false;
// if (y1 < 1 || y1 > r || y2 < 1 || y2 > r) return false;
if (x1 == x2 && y1 == y2) return false;
if (!arr[x1][y1] || !arr[x2][y2]) return false;
return sqr(x1 - x2) + sqr(y1 - y2) <= sqr(d);
}
int main(int argc, char **argv)
{
scanf("%d%d%d", &r, &c, &d);
graph.n = r * c * 2 + 2;
graph.s = 0;
graph.t = 801;
int tot_cnt = 0; // All lizards
for (int i = 1; i <= r; i++)
for (int j = 1; j <= c; j++)
arr[i][j] = read_number();
// Linking from source to entries
rep(i, 1, r) rep(j, 1, c)
if (read_position()) {
graph.add_edge(graph.s, get_pos(i,j), 1);
tot_cnt++;
}
// Building exit portals to sink
rep(i, 1, d) rep(j, d+1, r-d) {
graph.add_edge(get_pos(j,i) + 400, graph.t, infinit);
graph.add_edge(get_pos(j,c-i+1) + 400, graph.t, infinit);
}
rep(i, 1, d) rep(j, 1, c) {
graph.add_edge(get_pos(i,j) + 400, graph.t, infinit);
graph.add_edge(get_pos(r-i+1,j) + 400, graph.t, infinit);
}
// Building edges between nodes
rep(x1, 1, r) rep(y1, 1, c) rep(x2, x1-d, x1+d) rep(y2, y1-d, y1+d)
if (judge_node_pair(x1, y1, x2, y2))
graph.add_edge(get_pos(x1,y1) + 400, get_pos(x2,y2), infinit);
// Building flow limit in a single node
rep(i, 1, r) rep(j, 1, c)
if (arr[i][j])
graph.add_edge(get_pos(i,j), get_pos(i,j) + 400, arr[i][j]);
// Executing maximum flow
int res = graph.eval();
printf("%d\n", tot_cnt - res);
return 0;
}