• [BZOJ1189/Luogu3191][HNOI2007]紧急疏散EVACUATE


    题目链接:

    BZOJ1189

    Luogu3191

    (Duliu)网络流

    首先,很容易看出二分答案+网络流的方法:

    二分答案,将每一扇门拆成相应个数的节点。

    对于每一个人,与源点连边,流量为(1),代表一个人。

    然后对于每个人跑一遍最短路(边权均为(1),直接(BFS)),向(Ta)能够到达的门相应时间连边,容量为(1)

    每一扇门的每个时间节点向汇点连边,容量为(1),表示能够通过(1)人。

    于是一边吐槽水题,一边愉快的写了起来,然后发现(TLE)了。

    怎么优化呢?改善建图方式。

    对于每一扇门,时间(i)的节点向(i+1)连边,容量为(+infty),表示不能走这一扇就走下一扇,然后对于每个人,向(Ta)第一时间能够到达的节点连边即可,容量为(1)

    这么做以后,就跑得飞快了。

    话说这题好难写。。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    inline int Min(int a,int b){return a<b?a:b;}
    inline int Max(int a,int b){return a>b?a:b;}
    
    int n,m,Pec,Doc,St,Ed;
    int Ds[25][25][25][25],IDS[25][25][205],IDC;
    //Ds[i][j][x][y]为(i,j)到(x,y)的最短距离
    //IDS用于离散化节点编号
    int Head[130005],Next[200005],To[200005],Val[200005],En;
    int Dis[130005],Cnt[130005],Cur[130005],Pre[130005];
    char Map[25][25];
    struct Point{int x,y;};
    const int nx[]={-1,1,0,0},ny[]={0,0,-1,1};
    
    inline int ID(int x,int y,int z)
    {
    	if(!IDS[x][y][z])IDS[x][y][z]=++IDC;
    	return IDS[x][y][z];
    }
    
    inline void Add(int x,int y,int z)
    {
    	Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
    	Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
    }
    
    void BFS(int Sx,int Sy)//计算(Sx,Sy)到其它节点的距离
    {
    	int (*f)[25]=Ds[Sx][Sy];
    	std::queue<Point> q;
    	q.push((Point){Sx,Sy});
    	memset(f,0x3f,sizeof(int)*25*25);
    	f[Sx][Sy]=0;
    	for(;!q.empty();q.pop())
    	{
    		int x=q.front().x,y=q.front().y;
    		for(int i=0;i<4;++i)
    		{
    			int wx=x+nx[i],wy=y+ny[i];
    			if(wx<1||wx>n||wy<1||wy>m)continue;
    			if(f[wx][wy]<=f[x][y]+1||Map[wx][wy]=='X')continue;
    			f[wx][wy]=f[x][y]+1;
    			if(Map[wx][wy]=='.')q.push((Point){wx,wy});//门1s只能通过一人
    		}
    	}
    }
    
    void Build(int Tim)//建图,时间为Tim
    {
    	memset(Head,0,sizeof Head),En=1;
    	memset(IDS,IDC=0,sizeof IDS);
    	St=n*m+Tim*Doc+1,Ed=St+1;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(Map[i][j]=='D')
    				for(int k=1;k<=Tim;++k)
    				{
    					Add(ID(i,j,k),Ed,1);
    					if(k<Tim)Add(ID(i,j,k),ID(i,j,k+1),0x3f3f3f3f);//向后一条节点连边,容量Inf
    				}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(Map[i][j]=='.')
    			{
    				Add(St,ID(i,j,1),1);
    				for(int x=1;x<=n;++x)
    					for(int y=1;y<=m;++y)
    						if(Map[x][y]=='D')
    							if(Ds[i][j][x][y]<=Tim)
    								Add(ID(i,j,1),ID(x,y,Ds[i][j][x][y]),1);//向第一个门连边。
    			}
    }
    
    int Augment()
    {
    	int Res=0x3f3f3f3f;
    	for(int x=Ed;x!=St;x=To[Pre[x]^1])Res=Min(Res,Val[Pre[x]]);
    	for(int x=Ed,i;x!=St;x=To[i^1])Val[i=Pre[x]]-=Res,Val[i^1]+=Res;
    	return Res;
    }
    
    int ISAP()//简单的网络流
    {
    	memset(Dis,0,sizeof Dis);
    	memcpy(Cur,Head,sizeof Cur);
    	memset(Cnt,0,sizeof Cnt);
    	Cnt[0]=Ed;
    	int Flow=0,x=St;
    	while(Dis[St]<Ed)
    	{
    		if(x==Ed)Flow+=Augment(),x=St;
    		bool Flag=false;
    		for(int i=Cur[x],y;i;i=Next[i])
    			if(Val[i]&&Dis[y=To[i]]+1==Dis[x])
    				Flag=true,Cur[x]=Pre[y]=i,x=y,i=0;
    		if(Flag)continue;
    		int Wd=Ed-1;
    		for(int i=Head[x];i;i=Next[i])
    			if(Val[i])Wd=Min(Wd,Dis[To[i]]);
    		if(!--Cnt[Dis[x]])break;
    		++Cnt[Dis[x]=Wd+1],Cur[x]=Head[x];
    		if(x!=St)x=To[Pre[x]^1];
    	}
    	return Flow;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",Map[i]+1);
    		for(int j=1;j<=m;++j)
    			if(Map[i][j]=='.')++Pec;//人的数量
    			else if(Map[i][j]=='D')++Doc;//门的数量
    	}
    	if(!Pec)return puts("0"),0;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			BFS(i,j);
    	int l=0,r=202;
    	while(l<r)
    	{
    		int Mid=(l+r)>>1;
    		Build(Mid);
    		if(ISAP()==Pec)r=Mid;
    		else l=Mid+1;
    	}
    	if(l>200)puts("impossible");
    	else printf("%d
    ",l);
    	return 0;
    }
    
  • 相关阅读:
    v-chart使用的相关实例
    v-chart从后台获取数据
    算法题-单词搜索
    关于前端开发的一些小经验总结
    element的input中加入可点击的按钮
    vue.js的入门
    链表问题(算法题)
    同构词分类
    最长不降序子列的改编(dp数组的使用)
    Ubuntu安装Git
  • 原文地址:https://www.cnblogs.com/LanrTabe/p/10186892.html
Copyright © 2020-2023  润新知