1. 程式人生 > >1189. [HNOI2007]緊急疏散EVACUATE【最大流+枚舉或二分】

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

cst clas 開始 eva printf 矩陣 最短 方向 int

Description

發生了火警,所有人員需要緊急疏散!假設每個房間是一個N M的矩形區域。每個格子如果是‘.‘,那麽表示這是一 塊空地;如果是‘X‘,那麽表示這是一面墻,如果是‘D‘,那麽表示這是一扇門,人們可以從這兒撤出房間。已知門 一定在房間的邊界上,並且邊界上不會有空地。最初,每塊空地上都有一個人,在疏散的時候,每一秒鐘每個人都 可以向上下左右四個方向移動一格,當然他也可以站著不動。疏散開始後,每塊空地上就沒有人數限制了(也就是 說每塊空地可以同時站無數個人)。但是,由於門很窄,每一秒鐘只能有一個人移動到門的位置,一旦移動到門的 位置,就表示他已經安全撤離了。現在的問題是:如果希望所有的人安全撤離,最短需要多少時間?或者告知根本 不可能。

Input

第一行是由空格隔開的一對正整數N與M,3<=N <=20,3<=M<=20, 以下N行M列描述一個N M的矩陣。其中的元素可為字符‘.‘、‘X‘和‘D‘,且字符間無空格。

Output

只有一個整數K,表示讓所有人安全撤離的最短時間, 如果不可能撤離,那麽輸出‘impossible‘(不包括引號)。

Sample Input

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

Sample Output

3 這個題……有點像跳舞那個題emmmm……
答案肯定是滿足單調性的,所以我們可以枚舉
(可以和跳舞那個題一樣二分,不過二分就要每次重新添加邊,太麻煩)
先判斷impossible,BFS判斷就行。
建圖:超級源點-人-門-超級匯點
枚舉秒數,每一秒就在門和超級匯點間連一條容量1的邊,意味著當前秒這個門可以多出一個人了
若某個人到某個門耗費的時間為當前秒數,就在人和門間連一條容量為1的邊
之後跑一邊最大流,若最大流為人數的話就說明人可以全跑出去了
PS每次跑最大流的時候之前的Ans不能清零emmm
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define MAXM (100000+10)
#define MAXN (1010+10)
using namespace std;
struct node
{
    int Flow;
    int next;
    int to;
}edge[MAXM*2];
int Depth[MAXN],Q[MAXN];
int head[MAXN],num_edge;
int n,m,s,e=999,d,INF;
int a[MAXN][MAXN];
int num[MAXN][MAXN];
int dis[MAXN][MAXN];
int PEOPLE[MAXN],P_sum;
int DOOR[MAXN],D_sum;
int dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1};
int q[1005][3];
int Ans;
bool used[505][505];
char ch[1005];

void add(int u,int v,int l)
{
    edge[++num_edge].to=v;
    edge[num_edge].Flow=l;
    edge[num_edge].next=head[u];
    head[u]=num_edge;
}

bool Bfs(int s,int e)
{
    int Head=0,Tail=1;
    memset(Depth,0,sizeof(Depth));
    Depth[s]=1;
    Q[1]=s;
    while (Head<Tail)
    {
        int x=Q[++Head];
        for (int i=head[x];i!=0;i=edge[i].next)
            if (!Depth[edge[i].to] && edge[i].Flow>0)
            {
                Depth[edge[i].to]=Depth[x]+1;
                Q[++Tail]=edge[i].to;
            }
    }
    if (Depth[e]>0) return true;
    return false;
}

int Dfs(int x,int low)
{
    int Min,f=0;
    if (x==e || low==0)
        return low;
    for (int i=head[x];i!=0;i=edge[i].next)
        if (edge[i].Flow>0 && Depth[edge[i].to]==Depth[x]+1 && (Min=Dfs(edge[i].to , min(low,edge[i].Flow) )))
        {
            edge[i].Flow-=Min;
            edge[((i-1)^1)+1].Flow+=Min;
            f+=Min;
            low-=Min;
        }
    return f;
}

int Dinic(int s,int e)
{
//    int Ans=0;
    while (Bfs(s,e))
            Ans+=Dfs(s,0x7fffffff);
    return Ans;
}

void DISTANCE(int x,int y)
{
	int Head=0,Tail=1;
	memset(used,false,sizeof(used));
	q[1][1]=x;
	q[1][2]=y;
	used[x][y]=true;
	while (Head<Tail)
	{
		++Head;
		for (int i=1;i<=4;++i)
		{
			int xx=q[Head][1]+dx[i];
			int yy=q[Head][2]+dy[i];
			if (!used[xx][yy] && a[xx][yy])
			{
				used[xx][yy]=true;
				dis[num[x][y]][num[xx][yy]]=dis[num[x][y]][num[q[Head][1]][q[Head][2]]]+1;
				q[++Tail][1]=xx;
				q[Tail][2]=yy;
			}
		}	
	}
}

int main()
{
	memset(&INF,0x7f,sizeof(INF));
	int n,m,cnt=0;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
	{
		scanf("%s",ch);
		for (int j=1;j<=m;++j)
		{
			if (ch[j-1]==‘X‘)
				continue;
			num[i][j]=++cnt;
			if (ch[j-1]==‘.‘)
			{
				a[i][j]=1;
				PEOPLE[++P_sum]=num[i][j];
			}
			else
			{
				a[i][j]=2;
				DOOR[++D_sum]=num[i][j];
			}
		}
	}
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			if (a[i][j])
				DISTANCE(i,j);
	for (int i=1;i<=P_sum;++i)
	{
		bool flag=false;
		for (int j=1;j<=D_sum;++j)
			if (dis[PEOPLE[i]][DOOR[j]]!=0)
			{
				flag=true;
				break;
			}
		if (!flag)
		{
			printf("impossible\n");
			return 0;
		}
	}
	for (int i=1;i<=P_sum;++i)
	{
		add(0,PEOPLE[i],1);
		add(PEOPLE[i],0,0);
	}
	for (int i=1;i<=99999999;++i)
	{
		for (int j=1;j<=D_sum;++j)
		{
			add(DOOR[j],999,1);
			add(999,DOOR[j],0);
		}
		for (int j=1;j<=P_sum;++j)
			for (int k=1;k<=D_sum;++k)
				if (dis[PEOPLE[j]][DOOR[k]]==i)
				{
					add(PEOPLE[j],DOOR[k],1);
					add(DOOR[k],PEOPLE[j],0);
				}
		if (Dinic(0,999)==P_sum)
		{
			printf("%d",i);
			return 0;
		}
	}
}

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