题意:在一个有向图当中,现在每一条边带有一个容量,现在有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;
}