EOJ Monthly 2020.3 A.迷宫 (二分+最大流)

题目链接

https://acm.ecnu.edu.cn/contest/255/problem/A/

题意

给出有向图,有\(m\)个人在顶点\(S\),每天晚上你可以控制他们呆在原地不动或选择移动到下一个顶点(从一个顶点到相邻顶点恰好需要花费一个晚上时间),当然,你可以控制每一个青年有不一样的选择。每条边都有一个容量\(C\),代表着在同一个晚上最多\(C\) 个人可以穿过该边。
现在要求你在最少的时间内帮助所有人到达迷宫的出口\(T\)
保证给出的图,至少存在一条路径能从\(S\)\(T\)

思路

官方题解:
考虑二分答案。
我们现在要判断,所有人能否在\(mid\)天内走出去。
因为道路限制了每天通过的流量,我们不妨拆点,分层建图,把每一个点拆成 (标号,天数) 在相邻节点相邻的天数之间连边,然后跑最大流就好了。
比如样例的图我们这样去建:
Alt text

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 5100;
const int M = 2e6+10;
struct node{int u,v,w;}a[N];
struct edge{int to,cap,next;}e[M];
int head[N],tot;
int d[N],cur[N];
int k,n,m,S,T,s,t;
void add(int u,int v,int w)
{
    e[++tot].to=v,e[tot].cap=w;
    e[tot].next=head[u],head[u]=tot;
}
bool Bfs()
{
	memset(d,0,sizeof(d));
	queue<int>q;
	q.push(s);
	d[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=e[i].next)
		{
			int v=e[i].to;
			if(!d[v]&&e[i].cap)
			{
				d[v]=d[u]+1;
				q.push(v);
			}
		}
	}
	return d[t];
}
int Dfs(int u,int flow)
{
	if(u==t||!flow) return flow;
	int used=0;
	for(int i=cur[u];i!=-1;i=e[i].next)
	{
		cur[u]=i;
		int v=e[i].to;
		if(d[v]==d[u]+1&&e[i].cap)
		{
			int fl=Dfs(v,min(flow,e[i].cap));
			if(fl)
			{
				used+=fl;
				flow-=fl;
				e[i].cap-=fl;
				e[i^1].cap+=fl;
				if(!flow) break;
			}
		}
	}
	return used;
}
int Dinic()
{
	int sum=0;
	while(Bfs())
	{
		memcpy(cur,head,sizeof(head));
		sum+=Dfs(s,inf);
	}
	return sum;
}
bool judge(int x)
{
    tot=-1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        int u=a[i].u,v=a[i].v,w=a[i].w;
        for(int j=0;j<x;j++)
            add(u+j*n,v+(j+1)*n,w),add(v+(j+1)*n,u+j*n,0);
    }
    for(int i=1;i<=n;i++)
        for(int j=0;j<x;j++)
            add(i+j*n,i+(j+1)*n,inf),add(i+(j+1)*n,i+j*n,0);
    s=S,t=T+x*n;
    int tmp=Dinic();
    if(tmp>=k)return true;
    else return false;
}
int main()
{
    scanf("%d%d%d%d%d",&k,&n,&m,&S,&T);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
    int l=1,r=100,ans;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(judge(mid))r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2020-03-21 20:03  灰灰烟影  阅读(238)  评论(3编辑  收藏  举报