Lunch Time
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 602 Accepted Submission(s): 112
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.
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
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;
}