• 【bzoj1189】[HNOI2007]紧急疏散evacuate BFS最短路+动态加边网络流


    题目描述

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

    输入

    输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符'.'、'X'和'D',且字符间无空格。

    输出

    只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出'impossible'(不包括引号)。

    样例输入

    5 5
    XXXXX
    X...D
    XX.XX
    X..XX
    XXDXX

    样例输出

    3


    写题解之前先把一些该说的说了。

    1.经过小号交题测试,此题网上题解有一半是WA的

    2.另一半A的,绝大多数写的是二分

    3.事实证明,动态加边效率和二分不会差很多,而且相比二分代码非常短

    题解

    BFS最短路+动态加边网络流

    题目中描述“每一秒钟只能有一个人移动到门的位置”,我们其实不用这样去看,可以看成很多人可以同时站在门的位置,但是每秒最多只能有1个人从门的位置逃出。

    这样就提供了一个思路:先用最短的时间走到门的位置,再考虑逃出情况。

    所以需要求一下每个空地到每个门的最短路,由于边权为1,可以使用BFS求最短路。

    这里有一个坑点:存在一种门后边还有门的情况(例:

    4 4
    DXXD
    X..D
    X..X
    DXXX

    ans=4)

    其中发现按照上面的思路,一个人到门的位置,另一个人从该门出去,答案应该为3。(错在这里卡了1天QAQ)

    问题就出在门后之门。

    错误原因就在于走了门后之门。而事实上,门后之门是没有用的,因为按照正常思路,通过门后之门需要先经过前门,而经过前门就可以出去,无需下一步。

    所以在BFS时只能走空地,不能走门。

    这是此题难点之一。

    然后加边:s->空地,容量为1;空地->门的第t层,容量为1,其中t=空地到门的距离。

    枚举时间t,动态加边门的第t-1层->门的第t层,容量为inf;门的第t层->t,容量为1。

    判断满流即可。

    时间上界是n*m,因此只需要枚举到n*m就行。

    代码很丑。。。pos(i,j)表示点(i,j)对应的编号,loc(i,j)表示第i个门的第j层对应的编号。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define inf 0x3f3f3f3f
    #define pos(i , j) (i - 1) * m + j
    #define loc(i , j) (j == 0 ? pd[i] : n * m + 1 + (j - 1) * tot + i)
    using namespace std;
    queue<int> q;
    int n , m , map[600] , len[600][100] , pd[100] , tot;
    int head[200000] , to[2000000] , val[2000000] , next[2000000] , cnt = 1 , s , t , dis[200000];
    char str[25];
    void search(int k)
    {
    	int x , i;
    	for(i = 1 ; i <= n * m ; i ++ )
    		len[i][k] = -1;
    	while(!q.empty()) q.pop();
    	len[pd[k]][k] = 0 , q.push(pd[k]);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		if(x > m && map[x - m] == 1 && len[x - m][k] == -1) len[x - m][k] = len[x][k] + 1 , q.push(x - m);
    		if(x <= m * (n - 1) && map[x + m] == 1 && len[x + m][k] == -1) len[x + m][k] = len[x][k] + 1 , q.push(x + m);
    		if(x % m != 1 && map[x - 1] == 1 && len[x - 1][k] == -1) len[x - 1][k] = len[x][k] + 1 , q.push(x - 1);
    		if(x % m != 0 && map[x + 1] == 1 && len[x + 1][k] == -1) len[x + 1][k] = len[x][k] + 1 , q.push(x + 1);
    	}
    }
    void add(int x , int y , int z)
    {
    	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
    	int x , i;
    	memset(dis , 0 , sizeof(dis));
    	while(!q.empty()) q.pop();
    	dis[s] = 1 , q.push(s);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = head[x] ; i ; i = next[i])
    		{
    			if(val[i] && !dis[to[i]])
    			{
    				dis[to[i]] = dis[x] + 1;
    				if(to[i] == t) return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    int dinic(int x , int low)
    {
    	if(x == t) return low;
    	int temp = low , i , k;
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(val[i] && dis[to[i]] == dis[x] + 1)
    		{
    			k = dinic(to[i] , min(temp , val[i]));
    			if(!k) dis[to[i]] = 0;
    			val[i] -= k , val[i ^ 1] += k;
    			if(!(temp -= k)) break;
    		}
    	}
    	return low - temp;
    }
    int main()
    {
    	int i , j , sum = 0;
    	scanf("%d%d" , &n , &m) , s = 0 , t = n * m + 1;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		scanf("%s" , str + 1);
    		for(j = 1 ; j <= m ; j ++ )
    		{
    			if(str[j] == 'D') pd[++tot] = pos(i , j) , map[pos(i , j)] = 2;
    			else if(str[j] == '.') add(s , pos(i , j) , 1) , map[pos(i , j)] = 1 , sum ++ ;
    		}
    	}
    	for(i = 1 ; i <= tot ; i ++ ) search(i);
    	for(i = 1 ; i <= n * m ; i ++ )
    		if(map[i] == 1)
    			for(j = 1 ; j <= tot ; j ++ )
    				if(len[i][j] != -1)
    					add(i , loc(j , len[i][j]) , 1);
    	for(i = 1 ; i <= 2 * n * m ; i ++ )
    	{
    		for(j = 1 ; j <= tot ; j ++ ) add(loc(j , i - 1) , loc(j , i) , inf) , add(loc(j , i) , t , 1);
    		while(bfs()) sum -= dinic(s , inf);
    		if(!sum)
    		{
    			printf("%d
    " , i);
    			return 0;
    		}
    	}
    	printf("impossible
    ");
    	return 0;
    }
    

     

  • 相关阅读:
    ajax _02【XML响应,post请求】
    ajax_01【httpRequest.responseText】
    方法的定义【js函数】
    Promise基本用法
    promise
    筛选(1)
    ng-cli 中HTTP请求思路(1) (接口请求处理)
    PHP占用CPU过高几种思路
    关于tcpdump的那点事~
    虚拟机固定IP那点事
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6899863.html
Copyright © 2020-2023  润新知