题意是给一个n点m边的无向图,求最少去掉多少边使得最短路不存在,和最短路存在的情况下最多去掉多少边。
把所有最短路上的边搞进来,流量为1跑最大流,去掉多少边只要搞个DP就能找到最短路图上得最短的路,用m去减就行。
因为n<=2000,n^2复杂度随便搞都行。
坑点是有重边。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> #include <vector> #include <queue> using namespace std; #define maxn 2111 #define maxm 121111 #define INF 1000000000 struct node { int from, to, next, f; } edge[maxm], edge1[maxm]; int cnt; int head[maxn]; int n, m, u, v, w; int mp[maxn][maxn], num[maxn][maxn]; //记录重边 long long d1[maxn], d2[maxn]; bool vis[maxn]; void init () { memset (head, -1, sizeof head); memset (num, 0, sizeof num); cnt = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i == j) mp[i][j] = 0; else mp[i][j] = INF; } } } void dij (int from, int to) { memset (vis, 0, sizeof vis); for (int i = 1; i <= n; i++) d1[i] = (i == from ? 0 : INF); for (int i = 1; i <= n; i++) { long long cur = INF; int x; for (int j = 1; j <= n; j++) if (!vis[j] && d1[j] <= cur) { cur = d1[j]; x = j; } vis[x] = 1; for (int j = 1; j <= n; j++) { d1[j] = min (d1[j], d1[x]+mp[x][j]); } } return ; } void anti_dij (int from, int to) { memset (vis, 0, sizeof vis); for (int i = 1; i <= n; i++) d2[i] = (i == from ? 0 : INF); for (int i = 1; i <= n; i++) { long long cur = INF; int x; for (int j = 1; j <= n; j++) if (!vis[j] && d2[j] <= cur) { cur = d2[j]; x = j; } vis[x] = 1; for (int j = 1; j <= n; j++) { d2[j] = min (d2[j], d2[x]+mp[x][j]); } } return ; } void add_edge (int from, int to, int cap, int i) { edge[i].from = from; edge[i].to = to; edge[i].next = head[from]; edge[i].f = cap; //边上剩余流量 head[from] = i; return ; } int d[maxn]; //从起点到i的距离 bool vis_edge[maxm]; bool bfs (int s, int t) { memset (d, -1, sizeof (d)); queue <int> gg; while (!gg.empty ()) gg.pop (); gg.push (s); d[s] = 0; while (!gg.empty ()) { int x = gg.front (); gg.pop (); if (x == t) return 1; for (int i = head[x]; i != -1; i = edge[i].next) { node &e = edge[i]; if (d[e.to] == -1 && e.f) { d[e.to] = d[x] + 1; gg.push (e.to); } } } return 0; } int dfs (int u, int a, int t) { //来源 最短边 汇点 if (u == t || a == 0) return a; int flow = 0, f; //汇点增加的流量 最短边 for (int i = head[u]; i != -1; i = edge[i].next) { node &e = edge[i]; if (e.f && d[u] == d[e.to]-1) { f = dfs (e.to, min (a-flow, e.f), t); e.f -= f; edge[i^1].f += f; flow += f; if (flow == a) return flow; } } return flow; } long long maxflow (int s, int t) { long long ans = 0; while (bfs (s, t)) { ans += dfs (s, INF, t); } return ans; } int cnt1; int head1[maxn]; int son[maxn]; void dfs1 (int u) { son[u] = INF; int child = 0; for (int i = head1[u]; i != -1; i = edge1[i].next) { int v = edge1[i].to; child++; dfs1 (v); son[u] = min (son[u], son[v]+1); } if (child == 0) son[u] = 0; } void add_edge1 (int from, int to, int i) { edge1[i].from = from, edge1[i].to = to, edge1[i].next = head1[from], head1[from] = i; } int main () { while (scanf ("%d%d", &n, &m) == 2) { init (); for (int i = 1; i <= m; i++) { scanf ("%d%d%d", &u, &v, &w); if (mp[u][v] > w) { mp[u][v] = mp[v][u] = w; num[u][v] = num[v][u] = 1; } else if (mp[u][v] == w) { num[u][v]++; num[v][u]++; } } dij (1, n); anti_dij (n, 1); long long Min = d1[n]; cnt1 = 0; memset (head1, -1, sizeof head1); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i == j) continue; if (mp[i][j] != INF) { if (d1[i]+d2[j]+mp[i][j] == Min) { for (int t = 1; t <= num[i][j]; t++) { add_edge (i, j, 1, cnt++); add_edge (j, i, 0, cnt++); add_edge1 (i, j, cnt1++); } } } } } dfs1 (1); printf ("%lld %d\n", maxflow (1, n), m-son[1]); } return 0; } /* 6 6 1 2 1 2 3 2 3 6 3 1 4 3 4 5 2 5 6 1 2 2 1 2 1 1 2 1 8 9 1 2 1 2 3 1 3 4 1 4 8 1 2 7 1 1 5 1 5 6 1 6 7 1 7 8 1 */