Path(最小割,最短路图)

题意

给定一个\(n\)个点,\(m\)条边的有向图,每个边有边权\(w_i\)

需要删除一些边使得\(1\)\(n\)的最短路变长,求删除边的边权总和最小为多少。

数据范围

\(1 \leq T \leq 10\)

\(1 \leq n, m \leq 10000\)

\(1 \leq w_i \leq 10^9\)

思路

删去的这些边,肯定是最短路上的边。因此可以建立最短路图。

最短路图的建立方式是,先求\(1\)到其他点的最短距离\(dist1\),然后建反图,求\(n\)到其他点的最短距离\(dist2\)。然后枚举每条边\((u, v, w)\),若\(dist1[u] + dist2[v] + w = dist1[n]\),则这条边就留下。

建立完最短路图后,在最短路图上求最小割,即为答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<ll, int> pli;

const int N = 10010, M = 2 * N;
const ll inf = 1e18;

int n, m, S, T;
int h[N], e[M], ne[M], w[M], idx;
ll f[M];
int cur[N], d[N];
ll dist1[N], dist2[N];
bool st[N];

struct Edge
{
    int a, b, c;
}edge[M];

void add1(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void add2(int a, int b, ll c)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}

void dijkstra(int s, ll dist[])
{
    for(int i = 1; i <= n; i ++) dist[i] = inf;
    for(int i = 1; i <= n; i ++) st[i] = false;
    priority_queue<pli, vector<pli>, greater<pli> > heap;
    heap.push({0, s});
    dist[s] = 0;
    while(heap.size()) {
        auto t = heap.top();
        heap.pop();
        int ver = t.second;
        ll distance = t.first;
        if(st[ver]) continue;
        st[ver] = true;
        for(int i = h[ver]; ~i; i = ne[i]) {
            int j = e[i];
            if(dist[j] > distance + (ll)w[i]) {
                dist[j] = distance + (ll)w[i];
                heap.push({dist[j], j});
            }
        }
    }
}

bool bfs()
{
    memset(d, -1, sizeof(d));
    queue<int> que;
    que.push(S);
    d[S] = 0, cur[S] = h[S];
    while(que.size()) {
        int t = que.front();
        que.pop();
        for(int i = h[t]; ~i; i = ne[i]) {
            int ver = e[i];
            if(d[ver] == -1 && f[i]) {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if(ver == T) return true;
                que.push(ver);
            }
        }
    }
    return false;
}

ll find(int u, ll limit)
{
    if(u == T) return limit;
    ll flow = 0;
    for(int i = cur[u]; ~i && limit > flow; i = ne[i]) {
        cur[u] = i;
        int ver = e[i];
        if(d[ver] == d[u] + 1 && f[i]) {
            ll t = find(ver, min(f[i], limit - flow));
            if(!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

ll dinic()
{
    ll res = 0, flow;
    while(bfs()) {
        while(flow = find(S, inf)) {
            res += flow;
        }
    }
    return res;
}

int main()
{
    int TT;
    scanf("%d", &TT);
    while(TT --) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i ++) h[i] = -1;
        idx = 0;
        for(int i = 0; i < m; i ++) {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add1(a, b, c);
            edge[i] = {a, b, c};
        }
        dijkstra(1, dist1);
        for(int i = 1; i <= n; i ++) h[i] = -1;
        idx = 0;
        for(int i = 0; i < m; i ++) {
            int a = edge[i].a, b = edge[i].b, c = edge[i].c;
            add1(b, a, c);
        }
        dijkstra(n, dist2);
        for(int i = 1; i <= n; i ++) h[i] = -1;
        idx = 0;
        S = 1, T = n;
        for(int i = 0; i < m; i ++) {
            int a = edge[i].a, b = edge[i].b, c = edge[i].c;
            if(dist1[a] + dist2[b] + (ll)c == dist1[n]) {
                add2(a, b, (ll)c);
            }
        }
        printf("%lld\n", dinic());
    }
    return 0;
}
posted @ 2021-02-08 20:21  pbc的成长之路  阅读(131)  评论(0编辑  收藏  举报