题解:P3305 [SDOI2013] 费用流

第一问直接跑网络最大流就可以了,我们考虑第 \(2\) 问。很显然,\(\text{Bob}\) 一定会将所有花费 \(P\) 全部加在流量最大的边上。那么 \(\text{Alice}\) 的最优策略就出来了,那就是在最大流不变的情况下,让流量最大的边的流量尽可能小。

由于要让最大值最小,因此我们考虑二分答案。不过直接二分最大流不是那么好搞,因此我们考虑改变每条边的容量来间接改变最大流。一个显然的事实就是我们最终确定出的最大流一定至少有一条边流了 \(mid\) 的流量,否则还可以从原点找到增广路来增广。因此我们二分出容量上限 \(mid\),将所有容量大于 \(mid\) 的边改为 \(mid\),跑一遍网络最大流。如果这个时候最大流没有变,那么就将答案更新为 \(mid \times P\)

最后,这道题要用到实数二分,细节比较多,要多加小心啊。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e2 + 9, M = 5e3 + 9, INF = 1e18 + 9;
const double eps = 1e-6;
struct Edge{
    int v, nex;
    double c;
} e[M << 1];
int head[N], ecnt = 1;
void addEdge(int u, int v, double c){
    e[++ecnt] = Edge{v, head[u], c};
    head[u] = ecnt;
}
int cur[N], dep[N], n, m, p, s, t;
bool bfs();
double dfs(int u, double flow);
int fl[M];
double ans;
vector <int> vec;
signed main(){
    scanf("%lld%lld%lld", &n, &m, &p);
    s = 1, t = n;
    for(int i = 1; i <= m; i++){
        int u, v, c;
        scanf("%lld%lld%lld", &u, &v, &c);
        addEdge(u, v, c);
        vec.push_back(ecnt);
        fl[ecnt] = c;
        addEdge(v, u, 0);
    }
    while(bfs())
        ans += dfs(s, INF);
    double l = 0, r = 5e4, res = 0;
    while(fabs(l - r) > eps) {
        double mid = (l + r) / 2;
        for(int i : vec){
            e[i].c = min(mid, 1.0 * fl[i]);
            e[i ^ 1].c = 0; 
        }
        double tmp = 0;
        while(bfs())
            tmp += dfs(s, INF);
        if(fabs(tmp - ans) < 10 * eps){
            res = mid;
            r = mid - eps;
        } else
            l = mid + eps;
    }
    printf("%0.0f\n%0.4lf", ans, res * p);
    return 0;
}
posted @ 2025-06-25 15:55  Orange_new  阅读(9)  评论(0)    收藏  举报