这道题应用了网络流,主要考的是怎么转化为网络流求解,重点是怎么建图。
题意是给你一个n*m
网格,每个格子可能有一个柱子,也可能没柱子,每个柱子上有一个最大跳出次数,用完了这个柱子就废了,每个柱子上可能有一个蜥蜴(也可能没有,但只能最多存在一个(不仅初始是这样,过程中也要满足这一点)),蜥蜴的跳跃距离是d
,蜥蜴只能从柱子跳到另一个柱子上,两个柱子的距离定义为abs(x1-x2)+abs(y1-y2)
,这个距离必须小于等于d
才能跳。如果在当前柱子上跳跃一个合法距离可以跳出整个网格,那么这个蜥蜴就nb了,问你初始整个网格里面的蜥蜴有多少不nb。
柱子的这个次数暗示了点的限制条件,也暗示了这是个网络流问题(可以看出,在网络流中柱子只能是中间结点),运用拆点法思想,那么就把每个柱子拆成两个点,中间的连接边的权值就是这个次数。然后,我们要考虑柱子之间的跳跃关系,如果当前柱子的位置能跳出去,那么就把它和汇点连一条边(无容量限制),否则就和这个柱子能跳到的其他所有柱子都连一条边(根本不用考虑那个柱子还剩几次能跳,只要这两个柱子间距合法,就连边)(这个边无容量限制,但是要注意是当前柱子的后点连接其他柱子的前点(要不你那个柱子次数限制就不起作用了))。再然后,源点和初始有蜥蜴的柱子连一条权值为1
的边(这个1
表示这个柱子上有一只蜥蜴,就是网络中流来流去的东西)。
画个图吧,这个图对应样例中的第四个。
在实际代码中,我们没有判断当前柱子到达的那个位置点到底有没有柱子,不过这个无影响,因为如果那个位置点没柱子的话那条路肯定是死路、流不动。
还有一个问题,以上求解方法怎么保证每个柱子上最多只同时存在一只蜥蜴?
我的答案是,网络流的特性可以满足这一点,这些蜥蜴没必要同时去一个柱子(就算必须都去这个柱子),可以排队一个一个去。为什么呢?考虑最大流的EK
算法,是不是每次找一条增广路然后循环多次?那我们就想象一个蜥蜴的行进路线是一条路,这个东西走完了下个东西再走也ok。
还有就是要细心看样例输出,有三种输出句子。
dinic
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int MAX = 20 * 20 * 2 + 2 + 10;
const int INF = 1e9;
int T, n, m, d;
string str;
struct Edge
{
int from, to, flow, cap;
};
vector<Edge> ve;
vector<int> v[MAX];
int level[MAX];
int cur[MAX];
int flow;
int lizards;
void init()
{
ve.clear();
for (int i = 0; i < MAX; i++)
v[i].clear();
flow = lizards = 0;
}
void addEdge(int from, int to, int weight)
{
ve.push_back(Edge{ from,to,0,weight });
ve.push_back(Edge{ to,from,0,0 });
v[from].push_back(ve.size() - 2);
v[to].push_back(ve.size() - 1);
}
bool bfs(int dst)
{
queue<int> q;
memset(level, -1, sizeof level);
q.push(0);
level[0] = 0;
for (; !q.empty();)
{
int x = q.front();
q.pop();
for (int i = 0; i < v[x].size(); i++)
{
Edge& e = ve[v[x][i]];
if (level[e.to] < 0 && e.flow < e.cap)
{
level[e.to] = level[x] + 1;
q.push(e.to);
}
}
}
return level[dst] >= 0;
}
int dfs(int x, int dst, int f)
{
if (x == dst || f == 0) return f;
for (int& i = cur[x]; i < v[x].size(); i++)
{
Edge& e = ve[v[x][i]];
int f0;
if (level[e.to] == level[x] + 1 && (f0 = dfs(e.to, dst, min(f, e.cap - e.flow))) > 0)
{
e.flow += f0;
ve[v[x][i] ^ 1].flow -= f0;
return f0;
}
}
return 0;
}
int main()
{
scanf("%d", &T);
for (int cnt = 1; cnt <= T; cnt++)
{
init();
scanf("%d%d", &n, &d);
for (int i = 0; i < n; i++)
{
cin >> str;
m = str.size();
for (int j = 0; j < m; j++)
{
if (str[j] != '0')
{
int id = i*m + j + 1, id0;
addEdge(id, id + n*m, str[j] - '0');
if (i - d < 0 || i + d > n - 1 || j - d < 0 || j + d > m - 1)
addEdge(id + n*m, n*m * 2 + 1, INF);
else
{
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < m; jj++)
{
if (ii == i && jj == j) continue;
if (abs(ii - i) + abs(jj - j) <= d)
{
id0 = ii*m + jj + 1;
addEdge(id + n*m, id0, INF);
}
}
}
}
}
}
}
for (int i = 0; i < n; i++)
{
cin >> str;
for (int j = 0; j < m; j++)
{
if (str[j] == 'L')
{
int id = i*m + j + 1;
addEdge(0, id, 1);
lizards++;
}
}
}
for (; bfs(n*m * 2 + 1);)
{
memset(cur, 0, sizeof cur);
int temp;
for (; temp = dfs(0, n*m * 2 + 1, INF);)
flow += temp;
}
int ans = lizards - flow;
if (ans == 0) printf("Case #%d: no lizard was left behind.\n", cnt);
else if (ans == 1) printf("Case #%d: 1 lizard was left behind.\n", cnt);
else printf("Case #%d: %d lizards were left behind.\n", cnt, ans);
}
return 0;
}