hdu4807(最小费用流过程理解)

123 篇文章 0 订阅
46 篇文章 0 订阅

Lunch Time

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 602    Accepted Submission(s): 112


Problem Description
The campus of Nanjing University of Science and Technology can be viewed as a graph with N vertexes and M directed edges (vertexes are numbered from 0 to N - 1). Each edge has the same length 1. Every day, there are K students walking to the dinning-hall (vertex N - 1) from the teaching building (vertex 0) at lunch time. They all want reach the dinning-hall as soon as possible. However, each edge can only serve at most ci students at any time. Can you make arrangements for students, so that the last student can reach the dinning-hall as soon as possible? (It is assumed that the speed of the students is 1 edge per unit time)
 

Input
There are several test cases, please process till EOF.
The first line of each test case contains three integer N(2 <= N <= 2500), M(0 <= M <= 5000), K(0 <= K <= 10 9). Then follows M lines, each line has three numbers a i, b i, c i(0 <= c i <= 20), means there is an edge from vertex a i to b i with the capacity c i.
 

Output
For each test case, print an integer represents the minimum time. If the requirements can not be met, print “No solution”(without quotes) instead.
 

Sample Input
  
  
5 6 4 0 1 2 0 3 1 1 2 1 2 3 1 1 4 1 3 4 2 3 3 10 0 1 1 1 2 1 0 2 1 2 0 1
 

Sample Output
  
  
3 6 No solution

题意:
     给你一个有向图,每条边上都有每一时刻的最大流量,有k个人在点0,他们要去点n-1,问你最晚到达的那个人最快要多久。

思路:
     首先我们一定要明白一点,就是时间的影响,单纯的最大流如果在时间的基础上考虑没什么意义,而费用流呢,我们想象一下,如果把时间设成费用那么我们就可以吧流量和时间结合起来了,在费用流的过程中我们要知道,他每一次都是根据最短路去找新的路径的,也就是说路径在费用上来说是递增的(流量不一定),那么我们就可以根据这个特点来枚举一那些路径来过人,要明白,如果起点到终点有两条边,一条很近,一条很远,有可能大家都走近的路径(宁可排队走),也不走远的(所以直接最大流是错了),那么我们就可以枚举路径了,路径可以直接用费用流每次的路径,因为时间递增的,对于每一次我们能过的人是多少呢?这个地方很关键,对于每一条路径来说,如果当前的路径a距离是10,流量是15,那么当时间大于10的时候,每过一个时间单位,路径a都可以再过15个人,所以当前的时间段的总人数=之前的总人数+(当前长度-上一个长度)* 上一个的总流量 + 当前流量。那么如果现在当前这一部之前的所有路径当路径要花费的时间是多少呢?now = 当前长度+剩余的路径长度/当前总流量 (向上取整)
这样比较now和ans更新答案就行了,还有一个地方要明确,就是当总人数超过全图的最大流的时候答案就是 费用流中最长的路径 + 总人数/全图最大流 向上取整,这个地方不用特判,上面的想法里面包括在这里,说了只是为了便于理解。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<map>
#include<stdlib.h>
#include<time.h>
#include<stack>
#include<set>
using namespace std;
typedef long long ll;
const   int oo=1e9;
const   int mm=12000;
const   int mn=3000;
int node,src,dest,edge;
int ver[mm],flow[mm],cost[mm],nex[mm];
int head[mn],dis[mn],p[mn],q[mn],vis[mn];
void prepare(int _node,int _src,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0; i<node; i++)head[i]=-1,vis[i]=0;
    edge=0;
}
void addedge(int u,int v,int f,int c)///起点,终点,流量,费用
{
    ver[edge]=v,flow[edge]=f,cost[edge]=c,nex[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,cost[edge]=-c,nex[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;
    dis[q[r++]=src]=0;
    p[src]=p[dest]=-1;
    for(l=0; l!=r; (++l>=mn)?l=0:l)
        for(i=head[u=q[l]],vis[u]=0; i>=0; i=nex[i])
            if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i]))
            {
                dis[v]=tmp;
                p[v]=i^1;
                if(vis[v]) continue;
                vis[q[r++]=v]=1;
                if(r>=mn)r=0;
            }
    return p[dest]>-1;
}
void SpfaFlow(int k)/**源点到汇点的一条最短路即可行流,不断的找这样的可行流*/
{
    int i,delta,ans=oo,res=k,last_path_len=0,pessnum_per_sec=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;
        //cout<<"长度:"<<dis[dest]<<"    容量:"<<delta<<endl;
        res-=(dis[dest]-last_path_len)*pessnum_per_sec+delta;///该路径跑满时之前所有路径包括该路径跑过的总人数
        //cout<<"res="<<res<<endl;
        last_path_len=dis[dest];
        pessnum_per_sec+=delta;
        int temp=res;
        if(temp<0)temp=0;
        int n_ans=last_path_len+(int)ceil(1.0*temp/pessnum_per_sec);///res个人跑满该路径及之前所有路径所需最短时间
        if(n_ans<ans) ans=n_ans;
        if(res<=0) break;///没有跑满流就完成了
        //cout<<"ans="<<ans<<endl;
    }
    if(ans==oo)puts("No solution");
    else printf("%d\n",ans);
}
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        prepare(n,0,n-1);
        for(int i=0; i<m; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c,1);
        }
        if(!k)
            puts("0");
        else
            SpfaFlow(k);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值