【SPFA + DFS/BFS】最短路的一个拓展题

题目如下(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 – N­­3。在线输出即可。

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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值