题目链接
https://vjudge.net/problem/HDU-2732
题意
一个n*m矩阵,矩阵上有一些位置有方块,有的方块上有人,人的跳越距离为d,可以跳跃曼哈顿距离(abs(行差值)+abs(列差值)) 小于等于d的别的方块上。跳跃出迷宫即视为逃脱。每个方块有跳跃限制a,最多允许a次跳跃(从这个方块跳到别的方块减少一次)。问有几个人无法逃脱。
思路
所有方块拆成in和out,in向out连边,权为跳跃限制。s向有人的方块连边,权为1,对每个方块i做bfs,对于能在d步以内到达的方块j,将i的out向j的in连边,权为限制(其实比限制大的任何数都行)。在bfs的过程中判断一下能否跳出地图,可以的话把这个方块的out向t连边,权为限制,跑最大流,答案为总人数-最大流。
注意输出,no和1对应的是不加s和was,2以上对应的是加s和were。这破地方wa好几次,服了自己了。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2020;
const int maxe=200010;
//前向星部分
int head[maxn],cnt;
struct Edge{
int v;
int w;
int next;
}edge[maxe];
//用于BFS
struct Node{
int x,y,z;
}node;
//Dinic部分
int n,m,d,s,t,tot;
ll maxflow;
int deep[maxn];
int now[maxe];
//BFS部分
bool inq[30][30];
int dxy[][2]={{1,0},{-1,0},{0,1},{0,-1}};
//迷宫情况
int gp[30][30];
bool gp2[30][30];
//总初始化
void init(){
tot=0;
memset(head,-1,sizeof(head));
memset(gp,0,sizeof gp);
memset(gp2,0,sizeof gp2);
cnt=0;
maxflow=0;
return ;
}
//前向星加边
void add(int u,int v,int w){
//if(v==t)
//cout<<u<<" "<<v<<" "<<w<<endl;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
//Dinic
inline bool bfs(){
memset(deep,0x3f,sizeof(deep));
queue<int>q;
q.push(s);deep[s] = 0;now[s] = head[s];
while(q.size()){
int x = q.front();q.pop();
for(int i=head[x];i!=-1;i=edge[i].next){
int y=edge[i].v;
if(edge[i].w>0&&deep[y]==inf){
q.push(y);
now[y]=head[y];
deep[y]=deep[x]+1;
if(y==t) return 1;
}
}
}
return 0;
}
ll dfs(int x,int flow){
if(x==t) return flow;
ll ans = 0,k,i;
for(i=now[x];i!=-1&&flow;i=edge[i].next){
now[x]=i;
int y=edge[i].v;
if(edge[i].w>0&&(deep[y]==deep[x]+1)){
k=dfs(y,min(flow,edge[i].w));
if(!k) deep[y]=inf;
edge[i].w-=k;
edge[i^1].w+=k;
ans+=k;
flow-=k;
}
}
return ans;
}
void dinic(){
while(bfs())
maxflow+=dfs(s,inf);
}
//BFS部分
void find(int x,int y){
memset(inq,0,sizeof inq);
//拆点连边
add((x-1)*m+y,(x-1)*m+y+500,gp[x][y]);
add((x-1)*m+y+500,(x-1)*m+y,0);
queue<Node>q;
q.push({x,y,0});
inq[x][y]=1;
//这个方块能否跳出迷宫
bool es=0;
while(q.size()){
node=q.front(),q.pop();
if(node.z>d)
continue;
//方块i向j连边
if(gp[node.x][node.y]){
add((x-1)*m+y+500,(node.x-1)*m+node.y,gp[x][y]);
add((node.x-1)*m+node.y,(x-1)*m+y+500,0);
}
//四方向遍历
for(int i=0;i<4;i++){
int xx=node.x+dxy[i][0],yy=node.y+dxy[i][1];
if((xx<1||xx>n||yy<1||yy>m)&&node.z<d)
es=1;//可以跳出
else if(inq[xx][yy]==0){
inq[xx][yy]=1;
q.push({xx,yy,node.z+1});
}
}
}
//可以跳出就向t连边
if(es){
add((x-1)*m+y+500,t,gp[x][y]);
add(t,(x-1)*m+y+500,0);
}
}
int main(){
int tn;
scanf("%d\n",&tn);
for(int _=1;_<=tn;_++){
init();
//输入处理
scanf("%d%d",&n,&d);
getchar();
m=1;
char ch;
ch=getchar();
while(ch!='\n'){
gp[1][m]=ch-'0';
ch=getchar();
m++;
}
m--;
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
ch=getchar();
gp[i][j]=ch-'0';
}
getchar();
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ch=getchar();
if(ch=='L'){
tot++;
gp2[i][j]=1;
}
}
getchar();
}
//s和t设成绝对不可能编号的点
s=1501,t=s+1;
//1---n*m是方块in,501---n*m+500是方块out
//s向有人的块的in连边
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(gp2[i][j]){
add(s,(i-1)*m+j,1);
add((i-1)*m+j,s,0);
}
}
}
//对每个可以跳的方块跑bfs,in-out连边部分也放在了里面
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(gp[i][j]){
find(i,j);
}
}
}
dinic();
int ans=tot-maxflow;
//输出一定仔细,除了名词单复数还有was和were的区别。
printf("Case #%d: ",_);
if(!ans)
printf("no lizard was left behind.\n");
else if(ans==1)
printf("1 lizard was left behind.\n");
else
printf("%d lizards were left behind.\n",ans);
}
return 0;
}