题意:
给你一张节点为n、边数为m的有向图,每条边只能走一次,问该图的最短路有几条。
思路:
显然,我们先要找出所有与最短路有关的边,再建图跑最大流,关键是如何找出所有与最短路有关的边。对于这张有向图我们正向、反向各建一遍,用spfa算法各跑一遍最短路。那么与最短路有关的边满足 w + dis1[u] + dis2[v] = min_dis,即一条边的权值 + 源点到该边的前继节点u正向最短路 + 汇点到该边的后驱节点v的反向最短路 = 源点到汇点的最短路。
code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e3 + 5;
const int inf = 1e7 + 5;
struct edges{
int u, v, w, next;
} g[maxn * maxn], gp[maxn * maxn];
int head[maxn], dep[maxn], gap[maxn], cnt;
int head1[maxn], head2[maxn];
void init( int *hr, int n){
cnt = 0;
for(int i = 0; i <= n; i++){
hr[i] = -1;
}
}
void add(int* hr, edges* grap, int u, int v, int w){
grap[cnt].u = u;
grap[cnt].v = v;
grap[cnt].w = w;
grap[cnt].next = hr[u];
hr[u] = cnt++;
}
void bfs(int n, int t){
for(int i = 0; i <= n; i++){
dep[i] = -1;
}
queue<int> qu;
while(qu.size()) qu.pop();
qu.push(t);
dep[t] = 0;
while(qu.size()){
int u = qu.front();
qu.pop();
for(int i = head[u]; i != -1; i = g[i].next){
int v = g[i].v;
if(g[i ^ 1].w > 0 && dep[v] == -1) dep[v] = dep[u] + 1, qu.push(v);
}
}
for(int i = 0; i <= n; i++)
gap[i] = 0;
for(int i = 1; i <= n; i++)
gap[dep[i]]++;
}
int dfs(int n, int s, int t, int u, int limit){
if(u == t || limit == 0){
return limit;
}
int flow = 0, f;
bool flag = true;
for(int i = head[u]; i != -1; i = g[i].next){
int v = g[i].v;
if( g[i].w > 0 && dep[u] == dep[v] + 1 && (f = dfs(n, s, t, v, min(g[i].w, limit))) ){
g[i].w -= f;
g[i ^ 1].w += f;
limit -= f;
flow += f;
flag = false;
}
}
if(flag){
int min_dep = n - 1;
for(int i = head[u]; i != -1; i = g[i].next)
if(g[i].w > 0) min_dep = min(min_dep, dep[g[i].v]);
if(--gap[dep[u]] <= 0) dep[s] = n;
dep[u] = min_dep + 1;
gap[dep[u]]++;
}
return flow;
}
int isap(int n, int s, int t){
bfs(n, t);
int flow = 0;
while(dep[s] < n){
flow += dfs(n, s, t, s, inf);
}
return flow;
}
int dis1[maxn], dis2[maxn];
bool vis[maxn];
struct node{
int u, w;
bool operator < (const node& a) const {
return w > a.w;
}
};
int spfa(int* hr, int* dis, int n, int s, int t){
for(int i = 0; i <= n; i++){
dis[i] = inf;
vis[i] = false;
}
priority_queue<node> qu;
while(qu.size()) qu.pop();
dis[s] = 0;
qu.push({s, dis[s]});
while(qu.size()){
node p = qu.top();
qu.pop();
vis[p.u] = false;
for(int i = hr[p.u]; i != -1; i = gp[i].next){
int v = gp[i].v;
if(dis[v] > dis[p.u] + gp[i].w){
dis[v] = dis[p.u] + gp[i].w;
if(!vis[v]){
qu.push({v, dis[v]});
vis[v] = true;
}
}
}
}
if(dis[t] == inf) return -1;
return dis[t];
}
int main(){
int T, n, m;
int u, v, w, s, t;
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
init(head1, n);
init(head2, n);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &u, &v, &w);
add(head1, gp, u, v, w); //正向建图
add(head2, gp, v, u, w); //反向建图
}
scanf("%d%d", &s, &t);
int min_dis = spfa(head1, dis1, n, s, t); //正向跑最短路
spfa(head2, dis2, n, t, s); //反向跑最短路
int opt = cnt;
init(head, n);
for(int i = 0; i < opt; i = i + 2){
u = gp[i].u;
v = gp[i].v;
if(dis1[u] + dis2[v] + gp[i].w == min_dis) {
add(head, g, u, v, 1);
add(head, g, v, u, 0);
}
}
printf("%d\n", isap(n, s, t));
}
}
/*
input
3
7 8
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
5 7 1
6 7 1
1 7
6 7
1 2 1
2 3 1
1 3 3
3 4 1
3 5 1
4 6 1
5 6 1
1 6
2 2
1 2 1
1 2 2
1 2
*/
/*
output
2
1
1
*/