下笔春蚕食叶声。

【HDU 4807】 Lunch Time 最小费用最大流

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;
}
posted @ 2021-07-29 15:51  ACwisher  阅读(38)  评论(0编辑  收藏  举报