题解——[JSOI2007]重要的城市 floyd:最短路计数

~~~题面~~~

题解:

其实感觉还是比较妙的,第一眼看题想到floyd统计最短路条数,

注意到对于任意两点x,y而言,floyd将会枚举其最短路所可能经过的所有中转点,

因此我们可以直接分别统计对于所有二元组而言,最短路上必须经过的中转点,

最后遍历一次所有统计到的结果,并用bool数组标记一个地点是否被作为过中转点

最后再遍历一次bool数组,如果是中转点就输出即可

注意有多条最短路并不一定意味着这两个点之间的最短路就没有关键点,

因为这几条最短路可能有一个(或多个)共同用点,这时共同用点将成为关键点

首先来看一下我是怎么统计的:

 1                 if(f[i][j] > f[i][k] + f[k][j])
 2                 {
 3                     tot=0;//error!!!!要是找到一条更短的这个当然也要清空
 4                     f[i][j] = f[i][k] + f[k][j];
 5                     q[i][j][++tot] = k;
 6                     g[i][j] = 1;
 7                 }
 8                 else if(f[i][j] == f[i][k] + f[k][j])
 9                 {
10                     now = g[i][k] * g[k][j];
11                     if(now)//防止1 ~ 3 以1为中转这种情况
12                     {
13                         g[i][j] += now;
14                         tot = 0;
15                     }
16                 }

每次找到一条更短的路,就清空队列并重新统计最短路 + 放入中转点 + 标记最短路条数为1

如果找到了一条与当前最短路等长的路,那么就进行判断,

如果不是以自己为中转的话,那我就去掉所有中转标记(为什么这样是对的?为什么不会漏掉?)

好吧,貌似有点想明白了。

这个时候将会有两种情况:

1,出现了一条完全不同的最短路(也就是说两条最短路没有共同用点)

这时显然没有关键点,因为两条最短路不会相互影响,断了一条可以走另外一条,

所以这时是可以删的

2,出现了一条不同最短路,但这条最短路与之前的最短路有共同用点

这时虽然是有关键点的,但是我们还是可以删掉。

为什么?

这里我想了很久,其实是这样的:

注意到如果是这样的情况,那必然是类似于这样的图:

其中画红圈的显然就是关键点了,

此时由于有多条最短路(我们假设图中的所有路径都是最短的)

那么s ---> t将不会记录任何关键的,

但是我们可以观察到一个很妙的性质!

那就是关键点的出现必然是由于某条路径的唯一性所导致的,

例如图中的s ---> 2

如果s ---> 2的路径不是唯一的,那么1将不会成为关键点,

那么这意味这什么?

意味着虽然s ---> t没有统计到1,但是s ---> 2却可以统计到!

因为s ---> 2时已经没有边来干扰它们了,这条路径是唯一的!

所以我们依然可以删除,因为只要有一条边统计到了这个关键点就足够了。

在做这道题的时候我曾经陷入一个误区,虽然说比较弱智,但还是说一下吧:

就是这样一条路径:

1 ---> 2 ---> 3 ---> 4

为什么最短路计数不会统计到2条呢?

其实是因为k是放在最外层枚举的,这样的话,以2为中转的时候,3还没有成为过中转点,

因此2 ---> 4其实是不通的,因此此时不会统计到任何最短路,直到k == 3时才会统计到最短路。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 210
  5 #define inf 2139062143
  6 #define getchar() *o++
  7 /*虽然为一条链的时候两个端点可能不会统计全所有的中转?
  8 但是小的部分还是会统计到的?*/
  9 char READ[5000100],*o=READ;
 10 int n,m;
 11 int f[AC][AC],g[AC][AC];
 12 int q[AC][AC][AC],Head[AC][AC];
 13 bool z[AC];
 14 inline int read()
 15 {
 16     int x=0;char c=getchar();
 17     while(c > ‘9‘ || c < ‘0‘) c=getchar();
 18     while(c >= ‘0‘ && c <= ‘9‘) x=x*10+c-‘0‘,c=getchar();
 19     return x;
 20
 21 }
 22
 23 inline void upmin(int &a,int b)
 24 {
 25     if(b < a) a = b;
 26 }
 27
 28 void pre()
 29 {
 30     int a,b,c;
 31     n=read(),m=read();
 32     memset(f,127,sizeof(f));
 33     for(R i=1;i<=n;i++) f[i][i]=0;
 34     for(R i=1;i<=m;i++)
 35     {
 36         a=read(),b=read(),c=read();
 37         if(c < f[a][b])
 38         {
 39             f[a][b] = f[b][a] = c;
 40             g[a][b] = g[b][a] = 1;
 41         }
 42     }
 43 }
 44
 45 #define tot Head[i][j]
 46 void work()
 47 {
 48     int now;
 49     for(R k=1;k<=n;k++)
 50         for(R i=1;i<=n;i++)
 51         {
 52             if(f[i][k] == inf) continue;
 53             for(R j=1;j<=n;j++)
 54             {
 55                 if(f[k][j] == inf) continue;
 56                 if(f[i][j] > f[i][k] + f[k][j])
 57                 {
 58                     tot=0;//error!!!!要是找到一条更短的这个当然也要清空
 59                     f[i][j] = f[i][k] + f[k][j];
 60                     q[i][j][++tot] = k;
 61                     g[i][j] = 1;
 62                 }
 63                 else if(f[i][j] == f[i][k] + f[k][j])
 64                 {
 65                     now = g[i][k] * g[k][j];
 66                     if(now)//防止1 ~ 3 以1为中转这种情况
 67                     {
 68                         g[i][j] += now;
 69                         tot = 0;
 70                     }
 71                 }
 72             }
 73         }
 74 }
 75
 76 void getans()
 77 {
 78     bool done=false;
 79     for(R i=1;i<=n;i++)
 80         for(R j=1;j<=n;j++)
 81         {
 82             if(tot)
 83             {
 84                 for(R k=1;k<=tot;k++)
 85                     z[q[i][j][k]] = true;
 86                 done = true;
 87             }
 88         }
 89     for(R i=1;i<=n;i++)
 90         if(z[i]) printf("%d ",i);
 91     if(!done) printf("No important cities.\n");
 92 }
 93
 94 int main()
 95 {
 96 //    freopen("in.in","r",stdin);
 97     fread(READ,1,5000000,stdin);
 98     pre();
 99     work();
100     getans();
101 //    fclose(stdin);
102     return 0;
103 }
 

原文地址:https://www.cnblogs.com/ww3113306/p/9146320.html

时间: 2024-10-11 21:42:42

题解——[JSOI2007]重要的城市 floyd:最短路计数的相关文章

【啊哈!算法】算法6:只有五行的Floyd最短路算法

暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有4个城市8条公路,公路上的数字表示这条公路的长短.请注意这些公路是单向的.我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径.这个问题这也被称为“多源最短路径”问题. 现在需要一个数据结构来存储图的信息,我们仍然可以用一个4*4的矩阵(二维数组e)来存储.比如1号城市到2号城市的路程为2,则设e[1][2]

HDU 1385 Minimum Transport Cost(Floyd 最短路 打印路径)

HDU 1385 大意: 有N个城市,然后直接给出这些城市之间的邻接矩阵,矩阵中-1代表那两个城市无道路相连,其他值代表路径长度. 如果一辆汽车经过某个城市,必须要交一定的钱(可能是过路费). 现在要从a城到b城,花费为路径长度之和,再加上除起点与终点外所有城市的过路费之和. 求最小花费,如果有多条路经符合,则输出字典序最小的路径. 思路: Floyd求最短路,打印路径即可. 1 /*--------------------------------------------------------

题解——逃离僵尸岛(BFS+最短路+虚拟节点)

题解--逃离僵尸岛(BFS+最短路+虚拟节点) 一道很巧妙的最短路问题,细节也要注意 题面 Description 小a住的国家被僵尸侵略了!小a打算逃离到该国唯一的国际空港逃出这个国家. 该国有N个城市,城市之间有道路相连.一共有M条双向道路.保证没有自环和重边. K个城市已经被僵尸控制了,如果贸然闯入就会被感染TAT...所以不能进入.由其中任意城市经过不超过S条道路就可以到达的别的城市,就是危险城市.换句话说只要某个没有被占城市到某个被占城市不超过s距离,就是危险. 小a住在1号城市,国际

poj2607Fire Station(floyd最短路)

题目链接: 啊哈哈,点我带我 这道题目当时一看觉得很熟悉,但是后来越想越混乱,搞得最后题目都没搞清楚...比赛的时候不知道怎么想的,但是大致思想是对的.... 题意: 这道题目是讲原来镇上有若干个加油站,但是镇上的居民觉得消防站的距离李自己家太远,所以决定在居民点键一个消防站,要使离居民点的最大距离最小.. 思路:毫无疑问是最短路...但是这题数据太多..所以预处理的时候用floyd求出两个 任意两点间的距离,然后用加油站去更新到到各个居民点的最短距离..那么枚举所有加油站后就会得到所有到居民点

HDU 2833 WuKong(floyd最短路)

题目地址:HDU 2833 这题想到了最后是通过dis[s][t]==dis[s][i]+dis[i][j]+dis[j][t]的思路来判定是否属于最短路的一条..但是没想到可以用floyd来找最短路中的点数...最短路还是太渣了..好多性质都不会利用.. 这题的思路就是通过floyd对每两个点之间的最短路条数进行计数,然后通过上面的公式(对两条路线均要判定,都符合才说明都可以走),再找最短路中的最大点数. 代码如下: #include <iostream> #include <stdi

POJ2112Optimal Milking(二分+floyd最短路+网络流)

题目地址:http://poj.org/problem?id=2112 最近忙着预习课本备考,没怎么刷题,(我是真的有在好好看书..)不敲题还是手痒痒,马上就邀请赛了,还是每晚睡觉前都拿来刷题吧.白天的时间足够了. 话说这题调了一晚上...一直以为是几天没敲状态下滑..(虽然也没几天..)当发现错误的时候才发现原来是少敲了个字母...而且我一般很少在bfs的那个地方出错,错误地方也很隐蔽..所以找了一晚上,真是敲错一个字母成千古恨. 这题大概是职业生涯目前为止敲得最长的一道算法题了..(模拟题除

P1144 最短路计数 题解 最短路应用题

题目链接:https://www.luogu.org/problem/P1144 其实这道题目是最短路的变形题,因为数据范围 \(N \le 10^6, M \le 2 \times 10^6\) ,所以直接用Dijkstra算法是不行的,可以使用 Dijkstra+堆优化 或者 SPFA算法来实现. 我这里使用 SPFA算法 来实现 (不会Dijkstra堆优化囧) 这道题目因为需要计数,所以需要在dist数组基础上再开一个cnt数组,其含义如下: \(dist[u]\) :起点 \(1\)

HDU 1491 社交网络(最短路计数)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1491 题意:给出一个联通的无向图,边有权值.定义I(v)如下,计算每个点的I值. 思路:(1)最简单的就是那个floyd了...g[i][j]记录最短路长度,f[i][j]记录个数,不多说了: (2)下面说说我自己想出来的算法..枚举s和t,计算每个点到s和t的最短路..然后建立最短路树..之后求s到t的最短路个数..然后枚举删掉某个点再求最短路个数,则差值即为通过删掉点的最短路数目.

拼题 L2-001 紧急救援 最短路计数+记录路径

https://pintia.cn/problem-sets/994805046380707840/problems/994805073643683840 L2-001 紧急救援 (25 分) 作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图.在地图上显示有多个分散的城市和一些连接城市的快速道路.每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上.当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队. 输入格式: