题意是给一个n点m边的无向图,求最少去掉多少边使得最短路不存在,和最短路存在的情况下最多去掉多少边。
把所有最短路上的边搞进来,流量为1跑最大流,去掉多少边只要搞个DP就能找到最短路图上得最短的路,用m去减就行。
因为n<=2000,n^2复杂度随便搞都行。
坑点是有重边。
#include
#include
#include
#include
#include
#include
#include
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 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
*/