HDU 1733 [Escape] 分层图网络流+枚举时间

47 篇文章 1 订阅
25 篇文章 0 订阅

思路:

(1):把每个点按照天数拆成d个点。

(2):添加源汇点scr和sink。

(3):源点向第0天地图上人所在位置的点连一天容量为1的边。

(4):枚举时间Ti

(5):每次枚举只需向残留网络里重新添加一些新的点和边。然后再从源点向汇点跑最大流。

(6):直到前Ti天的最大流总和=总人数时,Ti就是所有的最少天数。



添加新的点和边:

如果当前枚举的天数为d,那么地图中每个位置第d-1天的点向四周和本身第d天的点(新添加的点)连一条容量为1 的边。


PS.一开始先用BFS判一下可达性。


CODE:

/*HDU_1733 Escape*/
/*分层图网络流+枚举时间*/
/*AC代码:171ms*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#define MAXN 20005
#define Lim 75
#define INF (1<<30)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
struct Node
{
    int x,y;
    Node(int x1,int y1)
    {x=x1;y=y1;}
    Node(){}
};
struct edge
{
    int u,v,w,next;
}E[2000000];
int head[MAXN],ecnt;
int gap[MAXN],cur[MAXN],dis[MAXN],pre[MAXN];
int N,M,scr,sink,vn,Ti,Sum,now;
int ans;
int dir[5][2]={0,1,0,-1,1,0,-1,0,0,0};
char map[20][20];
Node Q[200000];
int Head,Tail;
bool vis[20][20];
void Insert(int u,int v,int w)
{
    E[ecnt].u=u;
    E[ecnt].v=v;
    E[ecnt].w=w;
    E[ecnt].next=head[u];
    head[u]=ecnt++;
    E[ecnt].u=v;
    E[ecnt].v=u;
    E[ecnt].w=0;
    E[ecnt].next=head[v];
    head[v]=ecnt++;
}
bool BFS(Node s)
{
    int i,j;
    Node u,v;
    memset(vis,false,sizeof(vis));
    Head=Tail=0;
    vis[s.x][s.y]=true;
    Q[Head++]=s;
    while(Head!=Tail)
    {
        u=Q[Tail++];
        if(map[u.x][u.y]=='@') return true;
        for(i=0;i<4;i++)
        {
            int dx=u.x+dir[i][0];
            int dy=u.y+dir[i][1];
            if(dx>=0&&dx<N&&dy>=0&&dy<M&&map[dx][dy]!='#'&&!vis[dx][dy])
            {
                vis[dx][dy]=true;
                Q[Head++]=Node(dx,dy);
            }
        }
    }
    return false;
}
bool OK()
{
    bool ok=true;
    Node s;
    int i,j;
    for(i=0;i<N&&ok;i++)
    {
        for(j=0;j<M;j++)
        {
            if(map[i][j]=='X')
            {
                s.x=i;s.y=j;
                if(!BFS(s))
                {ok=false;break;}
            }
        }
    }
    return ok;
}
void Init()
{
    int i,j,u;
    memset(head,-1,sizeof(head));ecnt=0;
    for(i=0;i<N;i++)
        scanf("%s",map[i]);
    scr=0;sink=1;vn=2;
    Sum=0;
    for(i=0;i<N;i++)
    {
        for(j=0;j<M;j++)
        {
            if(map[i][j]=='X')
            {
                Sum++;
                u=i*M+j+2;
                Insert(scr,u,1);
            }
        }
    }
    now=(M*N);
    vn+=now;
}
int Sap(int s,int t,int n)//核心代码(模版)
{
	int ans=0,aug=INF;//aug表示增广路的流量
    int i,v,u=pre[s]=s;
    for(i=0;i<=n;i++)
	{
		cur[i]=head[i];
		dis[i]=gap[i]=0;
	}
    gap[s]=n;
	bool flag;
    while(dis[s]<n)
    {
		flag=false;
		for(int &j=cur[u];j!=-1;j=E[j].next)//一定要定义成int &j,why
		{
			v=E[j].v;
			if(E[j].w>0&&dis[u]==dis[v]+1)
			{
				flag=true;//找到容许边
				aug=min(aug,E[j].w);
				pre[v]=u;
				u=v;
				if(u==t)
				{
					ans+=aug;
					while(u!=s)
					{
						u=pre[u];
						E[cur[u]].w-=aug;
						E[cur[u]^1].w+=aug;//注意
					}
					aug=INF;
				}
				break;//找到一条就退出
			}
		}
		if(flag) continue;
		int mindis=n;
		for(i=head[u];i!=-1;i=E[i].next)
		{
			v=E[i].v;
			if(E[i].w>0&&dis[v]<mindis)
			{
				mindis=dis[v];
				cur[u]=i;
			}
		}
		if((--gap[dis[u]])==0) break;
		gap[dis[u]=mindis+1]++;
		u=pre[u];
	}
    return ans;
}

bool Judge(int Ti)
{
    int i,j,k,u,v,a,b,dx,dy;
    for(i=0;i<N;i++)
    {
        for(j=0;j<M;j++)
        {
            if(map[i][j]=='#') continue;
            u=i*M+j+1;
            a=(Ti-1)*N*M;
            b=a+N*M;
            u=a+u+1;
            for(k=0;k<5;k++)
            {
                dx=i+dir[k][0];
                dy=j+dir[k][1];
                if(dx>=0&&dx<N&&dy>=0&&dy<M&&map[dx][dy]!='#')
                {
                    v=dx*M+dy+1;
                    v=b+v+1;
                    Insert(u,v,1);
                }
            }

            if(map[i][j]=='@')
            {
                v=b+i*M+j+2;
                Insert(v,sink,1);
            }
        }
    }
    vn+=(N*M);
    now+=(N*M);
    int t=Sap(scr,sink,vn);
    ans+=t;
    return ans==Sum;
}
void Solve()
{
    if(Sum==0) {printf("0\n");return;}
    if(!OK())  {printf("-1\n");return;}
    ans=Ti=0;
    while(true)
    {
        Ti++;
        if(Judge(Ti)) break;
    }
    printf("%d\n",Ti);
}
int main()
{
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        Init();
        Solve();
    }
return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值