hdu4807Lunch Time(费用流)

本文介绍了一种使用网络流算法解决特定场景下人员就餐时间优化问题的方法。通过将人员从起点到终点的移动抽象成网络流问题,利用费用流算法找出最优路径,进而求得所有人抵达终点的最短时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:在一个有向图当中,现在每一条边带有一个容量,现在有K个人在起点,需要到终点去吃饭,询问这K个人最后一个人到达食堂的最小时间是多少。
想法:联想到普通的网络流,那么我们网络流可以很轻松的求出两个点之间的最大容量是多少,但是现在的问题就是刚开始在起步的时候那么最开始的容量是不可能到达最大的,因为人还在途中,假设我们从时间角度来分析这个问题,再联想到我们网络流求法,费用流当中,求出来的就是当然费用最小(也就是路径长度最短)的增广路径,那么从这个时刻之后每个单位时间都有这么多人能够到达餐厅,然后我们在求费用流的过程中,把所有这样找到的增广路径处理掉,按时间递增,如果处理完之后还是没有到达人数,那么接下去每一个时刻到达的人数就是最大流流量,然后接下去的部分直接算出来就可以了,要特别注意k = 0 时的trick。

#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxm=66666;//边的最大数量,为原图的两倍 
const int maxn=5555;
const int oo=1e9;
int src,dest,node,edge,num,num2;
int ver[maxm],cost[maxm],flow[maxm],_next[maxm],t[maxm];//node节点数,src源点,dest汇点,edge边数
int head[maxn],dis[maxn],p[maxn],q[maxn];
//head链表头,p记录可行流上节点对应的反向边,dis计算距离  ,q在模拟队列
//int h[55][55];
struct sa
{
    int time,flow;
} s[maxm];
bool vis[maxn]= {0};
void prepare(int _node,int _src,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0; i<node; ++i)
        head[i]=-1;
    edge=0;
    num=0;
    num2=1;
}
void addedge(int u,int v,int f,int c)//ver表示边的指向,flow是边的容量,cost是边的费用,_next是链表的下一条边
{
    ver[edge]=v,flow[edge]=f,cost[edge]=c,_next[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,cost[edge]=-c,_next[edge]=head[v],head[v]=edge++;

}
bool spfa()/**spfa 求最短路,并用 p 记录最短路上的边*/
{
    int i,u,v,l,r=0,tmp;
    for(i=0; i<=node; ++i)
    {
        dis[i]=oo;
         vis[i]=0;
         p[i]=-1;
    }
    dis[q[r++]=src]=0;
    for(l=0; l!=r; (++l==maxn)?l=0:l)
        for(i=head[u=q[l]],vis[u]=0; i>=0; i=_next[i])
            if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i]))//u->v容量未饱和,且能够松弛
            {
                dis[v]=tmp;
                p[v]=i^1;
                if(vis[v])
                    continue;
                vis[q[r++]=v]=1;
                if(r==maxn)
                    r=0;
            }
    return p[dest]>-1;
}
int spfaflow()/**源点到汇点的一条最短路即可行流,不断的找这样的可行流*/
{
    int i,delta,ret=0;
    while(spfa())
    {

        for(i=p[dest],delta=oo; i>=0; i=p[ver[i]])
            if(flow[i^1]<delta)
                delta=flow[i^1];//可分配最大流 为增广链上的最小容量边的容量
        for(i=p[dest]; i>=0; i=p[ver[i]])
        {
            flow[i]+=delta;//反向弧容量加上可分配最大流
            flow[i^1]-=delta;//正向弧容量减去可分配最大流
        }
        s[++num].time=dis[dest];

        s[num].flow=delta;
        ret+=delta*dis[dest];
    }
    return ret;
}
int main()
{
    int n,m,k,x,y,z;
    while(cin>>n>>m>>k)
    {
        prepare(n,0,n-1);
        for(int i=1; i<=m; i++)
        {
            cin>>x>>y>>z;
            addedge(x,y,z,1);
        }
       int sum=spfaflow();

        if(k==0)//k=0这块一定要特别注意
        {
            cout<<"0"<<endl;
            continue;
        }
        if(sum==0)
        {
            cout<<"No solution"<<endl;
            continue;
        }
        else
        {
            int l=0;
            int r=k/s[1].flow+s[1].time+1;//这里之前定的少了s[1].time,为什么要这么写呢
            //因为如果只走同一条路径的话,那么最长的时间是k/是s[1].flow,然后加上第一次到达这条路径的终点的时间s[1].time
            int mid=0;
            bool flag=0;
            while(l<=r)
            {
                long long ans=0;
                mid=(l+r)/2;
                for(int i=1; i<=num; i++)
                {
                    if(s[i].time<=mid)
                        ans+=(mid-s[i].time+1)*s[i].flow;//这里应定要加1,因为比如说第一次你两秒能到,5秒内,一共到了4波人,所以要加1
                }
                if(ans>=k)
                {
                    flag=1;
                    r=mid-1;
                }
                else l=mid+1;
            }
            if(flag==0)
            {
                cout<<"No solution"<<endl;
            }
            else
                cout<<l<<endl;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值