参考https://blog.csdn.net/liujc_/article/details/51179046
题意:
在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 <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
#define inf 0x7fffffff
const int INF = 0x3f3f3f3f;
struct Edge{
int v,w,nxt;
}g[100001];
int head[100001];
int cnt;
void addEdge(int u,int v,int w){
g[cnt].v = v;
g[cnt].w = w;
g[cnt].nxt = head[u];
head[u] = cnt;
++ cnt;
}
int n,m,x,y,z;
int ans,flow;
int dis[100001];
queue<int> q;
int S,T;
void init(){
memset(head,-1,sizeof(head));
memset(g,0,sizeof(g));
cnt = 0;
memset(dis,-1,sizeof(dis));
while(!q.empty()) q.pop();
ans = 0;
}
int bfs(){
memset(dis,-1,sizeof(dis));
while(!q.empty()) q.pop();
dis[S] = 0;
q.push(S);
while(!q.empty()){
int u = q.front();
q.pop();
for(int i=head[u];i!=-1;i=g[i].nxt){
int v = g[i].v;
if(dis[v]==-1 && g[i].w > 0){
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
return dis[T]!=-1;
}
int dfs(int u,int exp){
if(u==T) return exp;
int flow=0,tmp= 0;
for(int i=head[u];i!=-1;i=g[i].nxt){
int v = g[i].v;
if((dis[v] == (dis[u]+1)) && (g[i].w>0)){
tmp = dfs(v,min(exp,g[i].w));
if(!tmp) continue;
exp -= tmp;
flow += tmp;
g[i].w -= tmp;
g[i^1].w += tmp;
if(!exp) break;
}
}
return flow;
}
int distance(int x1,int x2,int y1,int y2)
{
return abs(x1-x2) + abs(y1-y2);
}
char mp1[30][30],mp2[30][30];
int main(){
int f,d,tmp,t,ca=1;
scanf("%d",&t);
while(t--){
init();
scanf("%d%d",&n,&d);
for(int i=0;i<n;i++) scanf("%s",mp1[i]);
for(int i=0;i<n;i++) scanf("%s",mp2[i]);
m=strlen(mp1[0]);
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)
{
if(i+1<=d||j+1<=d||n-i<=d||m-j<=d)//跳出边界
{
addEdge(i*m+j+1,T,mp1[i][j]-'0');
addEdge(T,i*m+j+1,0);
continue;
}
else
{
addEdge(i*m+j+1,i*m+j+1+n*m,mp1[i][j]-'0');
addEdge(i*m+j+1+n*m,i*m+j+1,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&&j==y) continue;
if(x<0||y<0||x>=n||y>=m||distance(i,x,j,y) > d || mp1[x][y]-'0' == 0) continue;
addEdge(i*m+j+1+n*m,x*m+y+1,INF);
addEdge(x*m+y+1,i*m+j+1+n*m,0);
}
}
}
}
}
int sum=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(mp2[i][j]=='L')
{
addEdge(S,i*m+j+1,1);
addEdge(i*m+j+1,S,0);
sum++;
}
}
}
while(bfs()){
ans += dfs(S,inf);
}
ans=sum-ans;
printf("Case #%d: ",ca++);
if(ans >1) printf("%d lizards were left behind.\n",ans);
else if(ans ==1) printf("1 lizard was left behind.\n",ans);
else if (ans == 0) printf("no lizard was left behind.\n");
}
}