题目如下(By cwx)
【题目描述】
给你一张含有 n 个点 m 条边的联通无向图,记录 1 号点到每个点的最短路长度,询问
去掉与 i 号相邻的所有边后,1 号点到多少个点的最短路长度改变,若不连通则也视为改
变。
【输入数据】
第一行两个正整数 n,m,
接下来 m 行,每行三个正整数数 i,j,k,表示一条边<i,j>,长度为 k。
【输出数据】
N 行,第 i 行表示去掉与 i 号相邻的所有边后,1 号点到多少个点的最短路长度改变。
【输入样例】
2 1
1 2 1
【输出样例】
1
1
【数据约定】
30% n <= 100,m <= 300
100% n <= 5000,m <= 20000,边权均为不超过 100 的正整数。
1.裸做法
每次孤立一些边,重新做spfa
2.转化为求必经点
到点n的最短路的必经点由多条(可能只有1条)可至n的最短路的点取交集得到。
可以依次递推得到,但是取交集的过程需要消耗一些时间与空间复杂度。
3.改进
将到各个点的最短路孤立出来新建一个图(到任意点的所有最短路都要孤立出来),且孤立出来的都是有向图,不妨设新图点数为N2。
为什么可以这样呢?因为若一个点不在到任一其他点的最短路上,那么即使删去了它也不会影响到达其余任何点的长度。
<1.如果不孤立出来:
无论是写dfs还是bfs,松弛次数就会相当大,相比之下不如重新做一遍spfa,则又绕回了裸做法。
<2.如果孤立出来:
i.假设写dfs,就只需要求得在新图上除去某点后,最多能到达的点数,不妨设最多为N3个点,则答案为N2 – N3。在线输出即可。
ii.假设写bfs,从要删的点开始,每扫到一个点就将其入度 -1 ,将入度为0的点进队。入度为0代表仅有唯一的一条已经被堵的最短路经过此点,于是将此点进队。所以最后队列的长度即为1号点到其他点最短路长度改变的个数。
其实主要算法就已经结束了,但是如何孤立出一个图却并没有讨论。事实上,利用第一次spfa的成果(dist数组),当dist[u] + worth[i] == dist[point[i]]时,则此边必定是到某点的最短路上的边。
dfs的时间在0.5s一个点左右,bfs在0.02s一个点左右,若是理解上述算法,那么造成差异的原因应该是显然的啵
不过编程复杂度差不多,有兴趣的可以都写写
贴下我没缩行过的code吧
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <memory>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 5000 + 5;
const int maxm = 20000 * 2 + 5;
const int status = 30000 + 5;
typedef int INT[maxm];
typedef int INTT[maxn];
int n, m, e(1), E(1), tnum(1);
INT ne, po; INTT ed;
INT next, point, worth;
INTT edge, dist, vp, tmp;
int s[status], L, R;
void init(), spfa(), solve();
void newgraph(int), dfs(int, int);
void addedge(int, int, int), link(int , int);
int main()
{
freopen("spfa.in", "r", stdin);
freopen("spfa.out", "w", stdout);
init();
spfa();
solve();
fclose(stdin); fclose(stdout);
return 0;
}
void init()
{
cin >> n >> m;
for (int i = 1, u, v, w; i <= m && scanf("%d%d%d", &u, &v, &w); ++i)
addedge(u, v, w), addedge(v, u, w);
}
void addedge(int u, int v, int w)
{
++e;
point[e] = v; next[e] = edge[u]; edge[u] = e; worth[e] = w;
}
void link(int u, int v)
{
++E;
po[E] = v; ne[E] = ed[u]; ed[u] = E;
}
void spfa()
{
memset(vp, 0, sizeof(vp));
memset(dist, 127, sizeof(dist));
for (L = R = 1, s[L] = vp[1] = 1, dist[1] = 0; L <= R; vp[s[L++]] = 0)
for (int i = edge[s[L]]; i; i = next[i])
if (dist[point[i]] > dist[s[L]] + worth[i])
{
dist[point[i]] = dist[s[L]] + worth[i];
if (!vp[point[i]]) vp[point[i]] = 1, s[++R] = point[i];
}
}
void newgraph(int u)
{
for (int i = edge[u]; i; i = next[i])
if (dist[point[i]] == dist[u] + worth[i])
{
link(u, point[i]);
if (!vp[point[i]]) { ++tnum; vp[point[i]] = 1; }
newgraph(point[i]);
}
}
void dfs(int u, int limt)
{
for (int i = ed[u]; i; i = ne[i])
{
if (po[i] == limt) continue;
if (!tmp[po[i]]) tmp[po[i]] = 1, ++tmp[0],
dfs(po[i], limt);
}
}
void solve()
{
//1
int tot = 0;
for (int i = 2; i <= n; ++i)
if (dist[i] != dist[0]) ++tot;
cout << tot << endl;
//makegraph
memset(vp, 0, sizeof(vp));
for (int i = 1; i <= n; ++i)
if (!vp[i]) newgraph(i);
//2
for (int i = 2; i <= n; memset(tmp, 0, sizeof(tmp)), ++i)
dfs(1, i), cout << tnum - 1 - tmp[0] << endl;
}