http://acm.hdu.edu.cn/showproblem.php?pid=2732
题目大意:
给了n和d,表示有n行的图,(列数没给),对于每一个人都可以最长跳d的距离,对于每一个柱子有一个限定条件,只能从这里跳出去多少次。给了两张图,一张表示柱子的限定条件,一张给了现在那些柱子上有人。问有几个人不能跳到图的外面。
思路:
最大流建图。将图的外面抽象为一个汇点t,对于图中本来就可以跳出去的点,就可以连一条边到t了。对于可以跳到图中别的点的,就可以从这个点连一条边过去。比较直观的想法就是这些边的容量都设为这个柱子限定的次数,最后跑最大流就好了。但是如果这个柱子可以跳到旁边很多的点上,每一个边的容量都是这个限定次数的话,等于限定的次数被放大了。那么就考虑拆点,将每个点拆成点1,点2,1->2连一条边容量为限定的次数。对于能跳到别的边,就从2连一条边到外面的点,容量为INF。
步骤:
拆点
1.将能够直接跳出去的点,拆出的第一个点连到t,容量为限定的次数。
2.不能直接跳出去的,将点1连到点2,容量为限定的次数。
3.对于能跳到别的点的,将这个点2连到别的点的点1,容量为INF。
4.对于柱子上有人的,从s连一条边到该点的点1,容量为1。
跑最大流。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
#define M 1009
typedef struct edge
{
int to,cap,rev;
};
vector<edge> G[M];
char mp1[30][30],mp2[30][30];
int level[M],iter[M];
int n,m,d;
int T;
int s,t,sum;
const int INF = 0x3f3f3f3f;
void init()
{
for(int i = 0;i <= 2*n*m+2;i++) G[i].clear();
sum = 0;
}
int to1(int i,int j)
{
return i*m + j+1;
}
int to2(int i,int j)
{
return to1(i,j) + n*m;
}
int dis(int x1,int x2,int y1,int y2)
{
return abs(x1-x2) + abs(y1-y2);
}
void add_edge(int u,int v,int cap)
{
G[u].push_back(edge{v,cap,G[v].size()});
G[v].push_back(edge{u,0,G[u].size()-1});
}
void bfs(int s)
{
memset(level,-1,sizeof(level));
level[s] = 0;
queue<int> q;
q.push(s);
while(!q.empty())
{
int v = q.front();
q.pop();
for(int i = 0;i < G[v].size();i++)
{
edge& e = G[v][i];
if(e.cap > 0 && level[e.to] < 0)
{
level[e.to] = level[v] + 1;
q.push(e.to);
}
}
}
}
int dfs(int u,int t,int f)
{
if(u == t) return f;
for(int& i = iter[u];i < G[u].size();i++)
{
edge& e = G[u][i];
if(e.cap > 0 && level[e.to] > level[u])
{
int d = dfs(e.to,t,min(f,e.cap));
if(d > 0)
{
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow()
{
int flow = 0;
for(;;)
{
bfs(s);
memset(iter,0,sizeof(iter));
if(level[t] < 0) return flow;
int f = 0;
while((f = dfs(s,t,INF)) > 0) flow += f;
}
}
int main()
{
scanf("%d",&T);
int k = 0;
while(k < T)
{
init();
k++;
scanf("%d %d",&n,&d);
for(int i = 0;i < n;i++) scanf("%s",mp1[i]);
m = strlen(mp1[0]);
for(int i = 0;i < n;i++) scanf("%s",mp2[i]);
s = 0;
t = 2*n*m+1;
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
if(mp1[i][j] - '0' > 0)
{
//add_edge(to1(i,j),to2(i,j),mp1[i][j]-'0');
if(i+1 <= d || j+1 <= d || n-i <= d || m-j <= d)
{
add_edge(to1(i,j),t,mp1[i][j]-'0');
continue;
}
else add_edge(to1(i,j),to2(i,j),mp1[i][j]-'0');
for(int ii = -d;ii <= d;ii++)
{
for(int jj = -d;jj <= d;jj++)
{
int x = i + ii;
int y = j + jj;
if(x == i && y == j) continue;
if(x < 0 || y < 0 || x >= n || y >= m || dis(i,x,j,y) > d || mp1[x][y]-'0' == 0) continue;
add_edge(to2(i,j),to1(x,y),INF);
}
}
}
}
}
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
if(mp2[i][j] == 'L')
{
add_edge(s,to1(i,j),1);
sum++;
}
}
}
int flow = max_flow();
int ans = sum - flow;
printf("Case #%d: ",k);
if(ans > 1) printf("%d lizards were left behind.\n",ans);
else if (ans == 1) printf("1 lizard was left behind.\n");
else if (ans == 0) printf("no lizard was left behind.\n");
}
return 0;
}