2019-CCPC-秦皇岛 E Escape (网络流)

18 篇文章 0 订阅
8 篇文章 0 订阅

题目链接

Escape


题意:

给定一个网格,用 01 01 01 表示, 1 1 1 的格子表示障碍,有 a a a 个机器人在第 0 0 0 行向下,在第 n + 1 n+1 n+1 行有 b b b 个出口,机器人只能直走,现在可以在空格上放置转弯转置,具体如题目中所示。问是否能够使得所有机器人走出迷宫。

思路:

由于机器人所在列一定不同,那么可以发现,对于每一个格子要么是转弯,要么竖直,要么水平,并且只会经过一次。 如果发现了这个性质题目就好解多了,只要将每一个点拆成两个点一个表示竖直,一个表示水平,在对每一个点连接水平和竖直就行了,跑一个最大流。
(网络流的构图思路还是不行)。

代码:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define inf 1e18
#define ll long long
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
struct Maxflow{
	int h[N],cur[N],ne[M],to[M],tot,deep[N],s,t,mx;
	ll flow[M],ans;
	inline void init(int a,int b,int c){
		s=a;t=b;mx=c;
		for(int i=0;i<M;i++)ne[i]=0;
		for(int i=0;i<N;i++)h[i]=0;
		tot=1;ans=0;
	}
	inline void addedge(int x,int y,int z){
		to[++tot]=y;ne[tot]=h[x],h[x]=tot,flow[tot]=z;
		swap(x,y);
		to[++tot]=y;ne[tot]=h[x],h[x]=tot,flow[tot]=0;
	}
	inline bool bfs(){
		for(int i=0;i<=mx;i++)deep[i]=-1;
		queue<int>q;
		q.push(s);deep[s]=0;
		for(int i=0;i<=mx;i++)cur[i]=h[i];
		while(!q.empty())
		{
			int now=q.front();q.pop();
			for(int i=h[now];i;i=ne[i]){
				int v=to[i];
				if(deep[v]==-1&&flow[i]>0)deep[v]=deep[now]+1,q.push(v);
			}
		}
		return deep[t]!=-1;
	}
	inline ll dfs(int u,ll op){
		if(u==t||op==0)return op;
		ll f=0,us=0;
		for(int i=cur[u];i;i=ne[i]){
			cur[u]=i;
			int v=to[i];
			if(deep[v]==deep[u]+1&&flow[i]>0){
				us=dfs(v,min(op,flow[i]));
				if(!us)continue;
				f+=us;op-=us;
				flow[i]-=us;flow[i^1]+=us;
				if(!op)break;
			}
		}
		if(!f)deep[u]=-1;
		return f;
	}
	inline ll maxflow(){
		while(bfs()){
			ans+=dfs(s,inf);
		}
		return ans;
	}
}F;
int T,n,m,a,b;
int get(int x,int y){
	return (x-1)*m+y;
}
char tu[200][200];
int main()
{
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d%d",&n,&m,&a,&b);
		int s=2*n*m+1,t=2*n*m+2;
		F.init(s,t,t);
		for(int i=1;i<=n;i++){
			scanf("%s",tu[i]+1);
		}
		for(int i=1,x;i<=a;i++){
			scanf("%d",&x);
			F.addedge(s,get(1,x),1);
		}
		for(int i=1,x;i<=b;i++){
			scanf("%d",&x);
			F.addedge(get(n,x),t,1e9);
		}
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			if(tu[i][j]!='1'){//注意是字符的1
				if(i+1<=n)F.addedge(get(i,j),get(i+1,j),1);
				if(i-1>=1)F.addedge(get(i,j),get(i-1,j),1);//竖直
				if(j+1<=m)F.addedge(get(i,j)+n*m,get(i,j+1)+n*m,1);
				if(j-1>=1)F.addedge(get(i,j)+n*m,get(i,j-1)+n*m,1);//水平
			}
		}
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			if(tu[i][j]!='1')F.addedge(get(i,j),get(i,j)+n*m,1),F.addedge(get(i,j)+n*m,get(i,j),1);//转弯
		}
		int ans=F.maxflow();
		if(ans==a){
			puts("Yes");
		}else puts("No");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值