javascript
【floyd】【bitset】洛谷 P1841 [JSOI2007]重要的城市 题解
?
? ??bitset玄學完美優化復雜度?
?
題目描述
參加jsoi冬令營的同學最近發現,由于南航校內修路截斷了原來通向計算中心的路,導致去的路程比原先增加了近一公里。而食堂門前施工雖然也截斷了原來通向計算中心的路,卻沒有使路程增加,因為可以找到同樣長度的路作替代。其實,問題的關鍵在于,路截斷的地方是交通要點。
?
同樣的情況也出現在城市間的交通中。某些城市如果出了問題,可能會引起其他很多城市的交通不便。另一些城市則影響不到別的城市的交通。jsoi冬令營的同學發現這是一個有趣的問題,于是決定研究這個問題。
?
他們認為這樣的城市是重要的:如果一個城市c被破壞后,存在兩個不同的城市a和b(a, b均不等于c),a到b的最短距離增長了(或不通),則城市c是重要的。
?
jsoi冬令營的同學面對著一張教練組交給他們的城市間交通圖,他們希望能找出所有重要的城市。現在就請你來解決這個問題。
?
輸入輸出格式
輸入格式:
第一行兩個整數N,M,N為城市數,M為道路數。
?
接下來M行,每行三個整數,表示兩個城市之間的無向邊,以及之間的路的長度。
?
輸出格式:
一行,按遞增次序輸出若干的數,表示重要的城市。
?
如果沒有點的話需要輸出一行
“No important cities.”
去掉引號。
?
輸入輸出樣例
輸入樣例#1:
4 4 1 2 1 2 3 1 4 1 2 4 3 2輸出樣例#1:
2說明
30%的數據:$N\le 20$;
?
60%的數據:$N\le 100$;
?
100%的數據:$N\le 200,M\le \frac{N\times (N-1)}{2},0<c\le 10000$。$c$即路的長度。
?
保證不出現重邊和自環
?
感謝@趙昕鵬 和@qq2477259579 提供程序
?
題解:
? ??因為Floyd是一種玄學DP思想,所以它的狀態更新來源有很多,在這個題里需要整理出它的階段性與轉移,看上去十分麻煩。而我們如果把Floyd當作最短路算法中的松弛,就是相當于在把兩段最短路拼接在一起,擁有它們合在一起的性質。
?
重要城市
? ??重要城市就是如果這個點被刪掉,那么最短路的長度就會改變。因此這個點一定在最短路上。而當兩點間的最短路有多條時,它們上的點不一定都是重要城市,經過分析我們可以這樣理解:設$(u,v)$間最短路條數為$k$,重要城市為$p$,那么這$k$條最短路一定都經過點$p$。用反例來說明,就是如果不是$k$條最短路都經過點$p$,那么去掉點$p$,還有剩下的最短路可以走,則不合法。
?
? ??因此我們可以開一個三維數組$im[i][j][k]$表示k在$latex i,j$的幾條最短路上。而我們用floyd做最短路計數也比較方便,一旦$k$所在的最短路數量與$(i,j)$間的最短路數量相同,那么$k$就一定是一個重要城市,判斷條件為$im[i][j][k]==cnt[i][j]\Rightarrow k$是重要城市。
? ??在上圖中,1→8最短路計數為3,其中除了起點和終點,被經過了3次的點的點有2和7,因此它們是這條路徑上的重要城市。
?
? ??因為floyd的時間復雜度為$O(N^3)$,而每次更新還要循環一個$N$,因此總時間復雜度為$O(N^4)$。
?
bitset優化
? ??我們在上面提到,狀態合并/更新需要額外枚舉一個$O(N)$,我們可不可以把這個$N$省掉,或者說優化一點呢?
?
? ??這時可以考慮bitset,bitset可以使常數優化32倍,這個題的$latex N$規模才200,優化一個32就快把一個$N$變成一個$\log N$了,這個題的數據規模還是可以承受的。不過bitset存的是二進制啊,可是上面提到的數組存的是計數啊。
?
? ??我們可以換一個方式想想,如果這兩個點之間已經找到了4條最短路,其中有3條經過點$p$,那此時$p$已經不合法了,就直接把它置為0,以后盡管所有路徑都經過$p$,它也不可能是關鍵城市。
?
? ??因此bitset中im[i][j][k]里面存的是,現有狀態下,k是不是i到j最短路上的關鍵城市。當更新(松弛)最短路時,關鍵城市是兩段最短路上的關鍵城市之并集;而更新最短路計數,也就是找到了一條新的最短路時,如上圖,就要取交集,因為一個城市只有在兩點間任何一條最短路上都存在,才能作為這兩點間的關鍵城市。而交集并集在位運算中就是and(&)和or(|),而點集有200,普通的位運算完成不了,就讓bitset來做。
?
? ??在一開始初始化時,把兩個連接在一起的點上的關鍵城市設為兩個端點,在floyd“松弛”最短路時,直接把兩段最短路的關鍵城市“拼起來”,就是新的最短路上的關鍵城市。最后判斷用$O(N^3)$遍歷,看一個點是否為某兩個點之間的關鍵城市,不過要注意不能與這兩個點重合,因為為了方便,一開始我們把起點和終點也定為關鍵城市(符合關鍵城市的一般定義)。
?
? ??因此這道題的總復雜度為$O(\frac{N^4}{32}+N^3)$
?
Code:
#include<cstdio> #include<cstring> #include<bitset> using std::bitset; bitset<210> im[210][210]; int f[210][210]; int is[210]; int main() {memset(f,0x3f,sizeof(f));int u,v,n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)f[i][i]=0;for(int i=1;i<=m;i++){scanf("%d%d",&u,&v);scanf("%d",&f[u][v]);f[v][u]=f[u][v];}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){im[i][j][i]=1;//初始化設兩端為重要城市im[i][j][j]=1;}for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(f[i][k]+f[k][j]==f[i][j])im[i][j]&=(im[i][k]|im[k][j]);//當更新計數時取交集else if(f[i][k]+f[k][j]<f[i][j])//當更新最短路時直接賦值為兩段的并集{f[i][j]=f[i][k]+f[k][j];im[i][j]=im[i][k]|im[k][j];}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)if(k!=i&&k!=j)//注意特判if(im[i][j][k])is[k]=1;int flag=0;for(int i=1;i<=n;i++)if(is[i]){flag=1;printf("%d ",i);}if(!flag)//注意判斷無解puts("No important cities.");return 0; }
轉載于:https://www.cnblogs.com/wjyyy/p/lg1841.html
總結
以上是生活随笔為你收集整理的【floyd】【bitset】洛谷 P1841 [JSOI2007]重要的城市 题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习视频教程
- 下一篇: 使用JS实现表单验证