题意
给定一个(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
", dinic());
}
return 0;
}