【HDU 4807】 Lunch Time 最小费用最大流
题意
有一张 \(n\) 个点 \(m\) 条边的图, \(k\) 个人从 \(1\) 出发去往 \(n\) 。
每条边有一个宽度 \(c_i\),花费时间为1,
不能有超过 \(c_i\) 个学生同时通过该边,但是可以先等待再走。(就像水龙头的感觉)
求所有人都到达终点的最短时间。
\(2 \le N \le 2500, 0 \le M \le 5000, 0 \le K\le 10^9, 0 \le c_i \le 20\)
题解
看到这个像水流的东西一般都会想到费用流,但是人们可以先等待再走,不能直接用。
考虑正常人走路,一定会选择一条用时最短的路,如果用时最短的满了,就考虑走用时次短的和等待再走哪个更快,如果次短的满了,就会考虑用时第三短的和等待次短再走哪个更快……以此类推。
注意到费用流每次新找到的路径都会花费越来越多。
可以考虑每次合并一条新路径。
假设最短的一条路径的用时 \(t_1\),宽度 \(c_1\),在 \(t_1\) 的时候会有 \(c_1\) 个人抵达,之后每秒都有 \(c_1\) 个人抵达。
如果只考虑最短,那么最少用时为 \(t_1 + \frac {(k - c _ 1)} {c_1}\)
假设次短的一条路径的用时 \(t_2\),宽度 \(c_2\),在 \(t_2\) 的时候会有 \(c_2\) 个人抵达,之后每秒都有 \(c_2\) 个人抵达。
在所有人等待次短的第一批人到的时候,一些人会走最短的路径先走,最少用时为 \(t_2 + \frac{k - c_ 1 - c_2- (t_2 - t_1) * c_ 1} {c_1 + c_2}\)
假设第三短的一条路径的用时 \(t_3\),宽度 \(c_3\),在 \(t_3\) 的时候会有 \(c_3\) 个人抵达,之后每秒都有 \(c_3\) 个人抵达。
在所有人等待第三短的第一批人到的时候,一些人会走最短、次短的路径先走,最少用时为 \(t_3 + \frac{k - c_ 1 - c_2 - c_ 3 - (t_2 - t_1) * c_ 1 - (t_3-t_2) * (c_1 + c_2) } {c_1 + c_2 + c_3}\)
以此类推,可以合并找到的所有路径。
注意特判 \(k = 0\),WA死我了。
代码
code
/*
Name: Lunch Time
Copyright: ACwisher
Author: ACwisher
Date: 29/07/21 14:39
Description: https://acm.hdu.edu.cn/showproblem.php?pid=480
*/
#include<bits/stdc++.h>
using namespace std;
#define mkp make_pair
#define fi first
#define se second
#define pii pair<int, int>
const int N = 2500 + 10, M = 5000 + 10, inf = 2000000000;
int n, m, k, s, t, pre[N], vis[N], dist[N], fl = 0;
int ans, sum, sum2;
int e = 1, hd[N], cur[N], nxt[M << 1], to[M << 1], val[M << 1], cost[M << 1];
void add(int u, int v, int w, int c){
to[++e] = v; val[e] = w; cost[e] = c;
nxt[e] = hd[u]; hd[u] = e;
}
void clear(){
ans = inf; sum = k; sum2 = 0;
e = 1;
memset(hd, 0, sizeof(hd));
memset(cur, 0, sizeof(cur));
memset(nxt, 0, sizeof(nxt));
memset(to, 0, sizeof(to));
memset(val, 0, sizeof(val));
memset(cost, 0, sizeof(cost));
return;
}
bool spfa(){
memset(dist, 0x3f, sizeof(dist));
memset(pre, -1, sizeof(pre));
queue<int>q; q.push(s); dist[s] = 0;
while(!q.empty()){
int u = q.front(); q.pop(); vis[u] = 0;
for(int i = hd[u]; i; i = nxt[i]){
int v = to[i];
if(val[i] > 0 && dist[u] + cost[i] < dist[v]){
pre[v] = i;
dist[v] = dist[u] + cost[i];
if(!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return ~pre[t];
}
pii lst;
void SSP(int &flow, int &costflow){
if(!k) ans = 0;
flow = 0; costflow = 0;
while(spfa()){
int mi = inf;
for(int p = pre[t]; p != -1; p = pre[to[p ^ 1]])
mi = min(mi, val[p]);
for(int p = pre[t]; p != -1; p = pre[to[p ^ 1]])
val[p] -= mi, val[p ^ 1] += mi;
flow += mi; costflow += 1ll * mi * dist[t];
//找到一条新的路径,时间为dist[t],贡献为mi
pii nw = mkp(dist[t], mi);
sum -= (nw.fi - lst.fi) * sum2 + nw.se; sum2 += nw.se;
ans = min(ans, nw.fi + (int)ceil(1.0 * ((sum < 0) ? 0 : sum) / sum2));
lst = nw;
if(sum < 1) break;
}
return;
}
int main(){
while(scanf("%d%d%d", &n, &m, &k) != EOF){
clear();
for(int i = 1, u, v, w; i <= m; i++){
scanf("%d%d%d", &u, &v, &w); u++; v++;
add(u, v, w, 1); add(v, u, 0, -1);
}
s = 1; t = n;
int ansflow, anscost; SSP(ansflow, anscost);
if(ans == inf) puts("No solution");
else printf("%d\n", ans);
}
return 0;
}