BZOJ 1189 【HNOI2007】 紧急疏散evacuate

题目链接:紧急疏散

  这薄脊题我代码不知不觉就写长了……

  这道题二分答案显然,然后用最大流\(check\)即可。设当前二分的答案为\(x\),那么把每扇门拆成\(x\)个点,第\(i\)个代表在第\(i\)个时刻从这个门走出去。然后把每个空地往可以到达的们的相应时间连边就可以了。判一下这张图是否满流即可。

  然后我们就需要先求出每个空地到门的距离……注意途中不能经过另外的门,否则会被BZOJ上加强的数据给卡掉……

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define INF 1000000000
#define maxn 200010
#define maxm 1000010
#define N 24

using namespace std;
typedef long long llg;

int zx[4]={1,-1,0,0},zy[4]={0,0,-1,1};
int n,m,nu[N][N],f[N*N][N*N],dor,tol,dep[maxn],d[maxn];
int head[maxn],next[maxm],to[maxm],c[maxm],tt,S,T;
char s[N][N]; bool w[N*N];

void link(int x,int y,int z){
	to[++tt]=y;next[tt]=head[x];head[x]=tt;
	to[++tt]=x;next[tt]=head[y];head[y]=tt;
	c[tt-1]=z; c[tt]=0;
}

bool bfs(){
    for(int i=1;i<=T;i++) dep[i]=-1;
    int ld=0,rd=0; dep[d[rd++]=S]=1;
    while(ld!=rd){
        int u=d[ld++];
        for(int i=head[u],v;v=to[i],i;i=next[i])
            if(c[i] && dep[v]==-1) dep[v]=dep[u]+1,d[rd++]=v;
    }
    return dep[T]!=-1;
}

int dfs(int u,int now){
    if(!now) return 0;
    if(u==T) return now;
    int low=0,res;
    for(int i=head[u],v;v=to[i],i;i=next[i])
        if(c[i] && dep[v]==dep[u]+1){
            res=dfs(v,min(now,c[i])); low+=res;
            c[i]-=res; c[i^1]+=res; now-=res;
        }
    if(!low) dep[u]=-1;
    return low;
}

bool check(int x){
	tt=1; S=tol*x+1; T=S+1;
	for(int i=1;i<=tol;i++)
		if(w[i])
			for(int j=1;j<=x;j++){
				link(tol*(j-1)+i,T,1);
				if(j!=x) link(tol*(j-1)+i,tol*j+i,INF);
			}
		else{
			link(S,i,1);
			for(int j=1;j<=tol;j++)
				if(w[j] && f[i][j]<=x) link(i,tol*(f[i][j]-1)+j,1);
		}
	int now=0; while(bfs()) now+=dfs(S,INF);
	for(int i=1;i<=T;i++) head[i]=0;
	return now==tol-dor;
}

int dx[N*N],dy[N*N]; bool vis[N][N];
void getdis(int x,int y){
	int u=nu[x][y]; if(w[u]) return;
	rep(i,1,n) rep(j,1,m) vis[i][j]=0;
	int ld=0,rd=0; dx[rd]=x,dy[rd++]=y; vis[x][y]=1;
	while(ld!=rd){
		int o=dx[ld],p=dy[ld++];
		for(int k=0,i,j,v;k<4;k++){
			i=o+zx[k],j=p+zy[k]; v=nu[i][j];
			if(i>=1 && i<=n && j>=1 && j<=m){
				if(vis[i][j]) continue;
				if(s[i][j]!=‘X‘) f[u][v]=f[u][nu[o][p]]+1;
				if(s[i][j]==‘.‘) dx[rd]=i,dy[rd++]=j,vis[i][j]=1;
			}
		}
	}
}

int main(){
	File("a");
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
		for(int j=1;j<=m;j++)
			if(s[i][j]!=‘X‘){
				nu[i][j]=++tol;
				dor+=(w[tol]=(s[i][j]==‘D‘));
			}
	}
	rep(i,1,tol) rep(j,1,tol) if(i!=j) f[i][j]=INF;
	rep(i,1,n) rep(j,1,m) if(nu[i][j]) getdis(i,j);
	int l=0,r=tol-dor+1,mid;
	while(l!=r){
		mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	if(l>tol-dor) printf("impossible");
	else printf("%d",l);
	return 0;
}
时间: 2024-10-23 15:56:14

BZOJ 1189 【HNOI2007】 紧急疏散evacuate的相关文章

BZOJ 1189: [HNOI2007]紧急疏散evacuate( BFS + 二分答案 + 匈牙利 )

我们可以BFS出每个出口到每个人的最短距离, 然后二分答案, 假设当前答案为m, 把一个出口拆成m个表示m个时间, 点u到出口v的距离为d, 那么u->v的[d, m]所有点连边, 然后跑匈牙利去check就行了...其实这道题挺好想但是码量还是挺大的.... ----------------------------------------------------------------------------- #include<cstdio> #include<cstring&

BZOJ 1189 [HNOI2007]紧急疏散evacuate

Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人).但是,由于门很窄,每一秒钟只能有

BZOJ 1189 HNOI2007 紧急疏散evacuate 二分答案+最大流

题目大意:给定一个m*n的地图,每个点有可能是空地.墙或者出口,每个空地初始站着一个人,每一时刻可以向周围走1格,门每一时刻只能通过一个人,求最短多少时间后所有人可以撤离 首先从每个出口出发开始广搜,得到每个空地到所有出口的距离 然后二分答案,每次建图如下: 从源点向每个空地一条流量为1的边 如果一个空地能在规定时间到达某个出口,就从这个空地出发向该出口链接一条流量为1的边 每个出口向汇点连接一条流量为时间的边 然后跑最大流验证即可 注意图有不连通的情况 所以广搜要清初值(这个没人会忘吧QAQ

【BZOJ】1189: [HNOI2007]紧急疏散evacuate(二分+bfs+网络流)

http://www.lydsy.com/JudgeOnline/problem.php?id=1189 表示完全不会QAQ.... 于是膜拜题解orz 二分时间........... 于是转换成判定性问题:即如何在有限时间内通过. 假设当前有t时间可供通过...那么每一个门最多能通过t个人........ 然后将所有能够到达门的点连边,容量为无限.... 然后源向每个点可行点连边..容量1.. 然后每个门向汇连边..容量为t.. 然后判断即可.... (一开始bfs写错了啊QAQ.. #inc

1189. [HNOI2007]紧急疏散EVACUATE【最大流+枚举或二分】

Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一 块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门 一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都 可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就是 说每块空地可以同时站无数个人).但是,由于门很窄,每一秒

bzoj千题计划132:bzoj1189: [HNOI2007]紧急疏散evacuate

http://www.lydsy.com/JudgeOnline/problem.php?id=1189 二分答案 源点向人连边,流量为1 门拆为mid个点,同一个门的第j个点向第j+1个点连边,流量为inf 若第i个人第k秒到达第j个门,第i个人向第j个门拆出的第k个点连边,流量为1 所有门向汇点连边,流量为1 用ISAP写的,真快,也真TM长 #include<queue> #include<cstdio> #include<algorithm> #include&

BZOJ1189: [HNOI2007]紧急疏散evacuate 二分+最大流

1189: [HNOI2007]紧急疏散evacuate Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1132  Solved: 412[Submit][Status][Discuss] Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且

AC日记——[HNOI2007]紧急疏散evacuate bzoj 1189

[HNOI2007]紧急疏散evacuate 思路: 处理每个人到门的最短路: 然后二分答案: s向人连边流量1: 人向门拆分后的点连边流量1(拆成400,前一个点连当前点流量INF): 然后门向t连边流量二分的答案: 如果最后流量等于人的个数,则true: 来,上代码: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorit

【BZOJ1189】[HNOI2007]紧急疏散evacuate 动态加边网络流

[BZOJ1189][HNOI2007]紧急疏散evacuate Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就

[HNOI2007]紧急疏散EVACUATE (湖南2007年省选)

[HNOI2007]紧急疏散EVACUATE 题目描述 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人).