CJOJ 1217 【HAOI2005】路由选择问题

【HAOI2005】路由选择问题

Description

X城有一个含有N个节点的通信网络,在通信中,我们往往关心信息从一个节点I传输到节点J的最短路径。遗憾的是,由于种种原因,线路中总有一些节点会出故障,因此在传输中要避开故障节点。
任务一:在己知故障节点的情况下,求避开这些故障节点,从节点I到节点J的最短路径S0。
任务二:在不考虑故障节点的情况下,求从节点I到节点J的最短路径S1、第二最短路径S2。

Input

第1行: N I J (节点个数 起始节点 目标节点)
第2—N+1行: Sk1 Sk2…SkN (节点K到节点J的距离为SkJ K=1,2,……,N)
最后一行: P T1 T2……Tp (故障节点的个数及编号)

Output

S0 S1 S2 (S1<=S2 从节点I到节点J至少有两条不同路径)

Sample Input

5 1 5
0 10 5 0 0
10 0 0 6 20
5 0 0 30 35
0 6 30 0 6
0 20 35 6 0
1 2

Sample Output

40 22 30

Hint

【约束条件】
(1)N<=50 N个节点的编号为1,2,…,N
(2)Skj为整数,Skj<=100,(K,J=1,2…,N 若Skj=0表示节点K到节点J没线路)
(3)P<=5  
样例解释:
Pic

Source

[HAOI2005]
图论,最短路,次短路

Solution

对于前面两个询问,直接spfa即可(同时记录最短路径),对于第三个询问,每一次删掉最短路径中的一条边,然后再spfa找到删过边后的最短边,即为次短边

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 60
#define LL long long
using namespace std;

struct node {
  int nxt, to, v;
} e[L << 1];
int n, st, se, P, p[L], minx = 2000000009, v, cnt, bj1 = 0, bj2 = 0, dis[L], head[L], pre[L];
bool vis[L], in[L];
queue <int> q;

inline void add(int a, int b, int c) {
  e[++cnt].nxt = head[a], e[cnt].to = b, e[cnt].v = c, head[a] = cnt;
}

inline void spfa(int pd) {
  while (!q.empty()) q.pop();
  if (pd == 1) memset(pre, 0, sizeof(pre));
  for (int i = 1; i <= n; ++i) in[i] = false, dis[i] = 2000000009;
  dis[st] = 0, in[st] = true, pre[st] = -1, q.push(st);
  while (!q.empty()) {
    int x = q.front();
    q.pop(), in[x] = 0;
    for (int i = head[x]; i; i = e[i].nxt) {
      int y = e[i].to;
      if ((x == bj1 && y == bj2) || (x == bj2 && y == bj1)) continue;
      if (vis[y]) continue;
      if (dis[y] > dis[x] + e[i].v) {
	dis[y] = dis[x] + e[i].v;
	if (pd) pre[y] = x;
	if (!in[y]) in[y] = 1, q.push(y);
      }
    }
  }
}

int main(){
  freopen("CJOJ1217.in", "r", stdin);
  freopen("CJOJ1217.out", "w", stdout);
  scanf("%d %d %d", &n, &st, &se);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j) {
      scanf("%d", &v);
      if (v == 0) continue;
      else add(i, j, v);
    }
  scanf("%d" ,&P);
  for (int i = 1; i <= P; ++i) scanf("%d", &p[i]), vis[p[i]] = 1;
  spfa(1), printf("%d ", dis[se]);
  memset(vis, 0, sizeof(vis));
  spfa(1), printf("%d ", dis[se]);
  v = se;
  while (pre[v] != -1) {
    bj1 = v, bj2 = pre[v];
    spfa(0);
    minx = min(minx, dis[se]);
    v = pre[v];
    pre[st] = -1;
  }
  printf("%d", minx);
  return 0;
}


Summary

注意找次短边的方式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值