题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2732
题意:
给你一个网格,网格上的一些位置上有一只蜥蜴,所有蜥蜴的最大跳跃距离是d,如果一只蜥蜴能跳出网格边缘,那么它就安全了.且每个网格有一个最大跳出次数x,即最多有x只蜥蜴从这个网格跳出,这个网格就再也不能有蜥蜴进来了.问你最少有多少只蜥蜴跳不出网格.
输入描述:输入矩阵的行M和跳跃值D,接着输入两个矩阵(列数自己求),第一个矩阵是每个柱子的耐久值,下面一个矩阵有'L'的表示这个柱子上有一只蜥蜴。
给出来自大神的分析:传送门http://blog.csdn.net/u013480600/article/details/38964749
分析:
建图:
源点S编号0,网格的每个格子分成两个点i和i+n*m(n和m为网格的行和列数,其实i编号点是表示蜥蜴进来,而i+n*m编号的点是表示蜥蜴出去).汇点t编号n*m*2+1.
如果格子i上有蜥蜴,那么从s到i有边(s,i,1).
如果格子i能承受x次跳出,那么有边(i,i+n*m,x)
如果从格子i能直接跳出网格边界,那么有边(i+n*m,t,INF)
如果从格子i不能直接跳出网格,那么从i到离i距离<=d的网格j有边(i+n*m,j,INF). 注意这里的距离是abs(行号之差)+abs(列号之差)
最终我们求出的最大流就是能跳出网格的蜥蜴数.
原题中提到:任意时刻每个柱子上最多只有1只蜥蜴在上面,那么我们上面的解法会不会与这个要求冲突呢?
不会的,假设有k只蜥蜴能出去,那么一定存在一个符合上面要求的解,使得这k只蜥蜴按顺序出去,在任意时刻每个柱子上最多只有1只蜥蜴.(假设在某个时刻,蜥蜴j想出去,它跳到了柱子h上,但是柱子h上已经有蜥蜴了,那么这样就违反了上面的要求. 其实我们可以这么想,我们为什么不让柱子h上的蜥蜴先按照蜥蜴j以前的逃跑路线出去,然后再让蜥蜴j到柱子h上去替代之前的蜥蜴,那么这样既不违反规则,也得到了解)
收获:
1.这是第一次接触接触到拆点这个技巧。本题中对于那些不能跳出去但是又有柱子的点,那么我们就去按照跳跃距离搜寻有没有其他的柱子能够去跳跃,如果能够找到的话,那么连接这两点,并且将容量控制为弧尾节点的柱子耐久度,这是因为一条弧只能约束一个顶点。如果这个柱子可以跳到旁边很多的点上,每一个边的容量都是这个限定次数的话,等于限定的次数被放大了,所以我们需要进行拆点,点内之间的流量为该柱子本身的耐久度。
2.上面提到最多只有1只蜥蜴在柱子上,但是思考之后,用水是能够流动的思想来思考的话,其实这并不会影响求解的。就像HDU6017一样,思考交换的是哪个2并不重要。
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define PI acos(-1.0)
#define eps 1e-8
#define pb push_back
#define mp make_pair
#define MII map<int,int>::iterator
#define MLL map<LL,LL>::iterator
#define pii pair<int,int>
#define SI set<int>::iterator
#define SL set<LL>::iterator
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
#define bug printf("bug-------bug-------bug\n")
using namespace std;
typedef long long ll;
typedef unsigned long long ul;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int maxn = 1000;
const int INF = 0x3f3f3f3f;
struct Edge
{
int from, to, cap, flow;
Edge(){}
Edge(int _from, int _to, int _cap, int _flow):from(_from), to(_to), cap(_cap), flow(_flow){}
};
struct Dinic
{
int n, m, s, t;
vector<Edge> edges;
vector<int> G[maxn];
int cur[maxn];
int d[maxn];
bool vis[maxn];
void init(int n, int s, int t)
{
this->n = n;this->s = s;this->t = t;
edges.clear();
for(int i = 0; i < n; i++)
G[i].clear();
}
void AddEdge(int from, int to, int cap)
{
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
queue<int> q;
q.push(s);
memset(vis, false, sizeof(vis));
d[s] = 0;
vis[s] = true;
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = 0; i < G[u].size(); i++)
{
Edge &e = edges[G[u][i]];
if(!vis[e.to] && e.cap > e.flow)
{
d[e.to] = d[u] + 1;
vis[e.to] = true;
q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, int maxflow)
{
if(x == t || maxflow == 0)
return maxflow;
int flow = 0, f;
for(int& i = cur[x]; i < G[x].size(); i++)
{
Edge &e = edges[G[x][i]];
if(d[e.to] == d[x] + 1 && (f = DFS(e.to, min(maxflow, e.cap-e.flow) ) ) > 0)
{
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
maxflow -= f;
if(maxflow == 0)
break;
}
}
return flow;
}
int max_flow()
{
int ans = 0;
while(BFS())
{
memset(cur, 0, sizeof(cur));
ans += DFS(s, INF);
}
return ans;
}
}DC;
int main()
{
int T;scanf("%d", &T);
for(int kase = 1; kase <= T; kase++)
{
int n, m, d, src, dst;
int sum = 0;
scanf("%d%d", &n, &d);
for(int i = 1; i <= n; i++)
{
string s;
cin >> s;
if(i == 1)
{
m = s.size();
src = 0;
dst = 2*n*m+1;
DC.init(2*n*m+2, src, dst);
}
for(int j = 0; j < s.size(); j++)if(s[j]-'0' > 0)
{
int id = (i-1)*m + j + 1;
DC.AddEdge(id, id+n*m, s[j]-'0');
if(i <= d || i+d > n || j < d || j+d >= m)
DC.AddEdge(id+n*m, dst, INF);
else
{
for(int k = 1; k <= n; k++)
for(int h = 0; h < m; h++)
{
int id2 = (k-1)*m + h + 1;
if(id == id2)
continue;
if(abs(i-k) + abs(j-h) <= d)
DC.AddEdge(id+n*m, id2, INF);
}
}
}
}
for(int i = 1; i <= n; i++)
{
string s;
cin >> s;
for(int j = 0; j < s.size(); j++)
{
if(s[j] == 'L')
{
int id = (i-1)*m+j+1;
sum++;
DC.AddEdge(src, id, 1);
}
}
}
int ans = sum - DC.max_flow();
if(ans == 0) printf("Case #%d: no lizard was left behind.\n", kase);
else if(ans == 1) printf("Case #%d: 1 lizard was left behind.\n", kase);
else printf("Case #%d: %d lizards were left behind.\n", kase, ans);
}
return 0;
}