题目
题目链接:http://codeforces.com/problemset/problem/416/E
题目来源:寒假总结赛王老板出的题。
简要题意:给定一张图,求出有多少边至少属于一条 i,j(i<j) 间的最短路。
题解
这是一道图论的难题,需要想到整个处理的技巧才行。关键一点是最短路的子路也是最短路。
这题首先需要预处理下Floyd。
然后求出 i→j 的最短路中有多少指向 j 的边
cnt[i][j] ,用 δ(i,j) 表示 i,j 间的最短路。δ(i,k)=e(k,j)+δ(i,j) 则我们可以确定 k→j 的边一定是在最短路上的。
然后我们枚举所有点判断下 cnt 就做出来了。
若 δ(i,j)=δ(i,k)+δ(k,j) 则 i→k→j 可以产生最短路。
此时我们加上 cnt[i][k] 就是把以 k 为终点的边加上,枚举
k 就能得到结果。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
// head
const int N = 505;
const int INF = 0x3f3f3f3f;
int e[N][N];
int dis[N][N];
int cnt[N][N];
void Floyd(int n) {
for (int i = 0; i < n; i++) {
dis[i][i] = 0;
}
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
}
int main() {
memset(e, INF, sizeof e);
int n, m, u, v, c;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &u, &v, &c);
u--, v--;
e[u][v] = e[v][u] = c;
}
memcpy(dis, e, sizeof dis);
Floyd(n);
// 处理出指向j的边中在i->j最短路上的个数
// 注意判断条件
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
if (e[k][j] != INF && dis[i][k] + e[k][j] == dis[i][j]) {
cnt[i][j]++;
}
}
}
}
// 由于最短路的传递性,i->k k->j也为最短路
// 由于做出来是入的cnt,因而枚举点作为入的点
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
int ans = 0;
for (int k = 0; k < n; k++) {
if (dis[i][k] + dis[k][j] == dis[i][j]) {
ans += cnt[i][k];
}
}
printf("%d ", ans);
}
}
return 0;
}