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