AtCoder Beginner Contest 243 E - Edge Deletion( 刪除冗餘最短路)
阿新 • • 發佈:2022-03-25
傳送門
給出一個\(n\)個點\(m\)條邊的無向圖,求在保證任意兩點的最短路不變的情況下最多可以刪除幾條邊。其中 \(n\leq300\)
解決這個問題關鍵在於一條結論:對於一條連結\(u\)、\(v\)長度\(w\)的邊\(i\),若存在除\(u\)和\(v\)之外的點\(x\)滿足\(dis[u][x]+dis[x][v]\leq w\)則這條邊刪除之後對個點之間最短路沒有影響。
因為\(dis[u][x]+dis[x][v]\leq w\)這條邊完全可以使用另外一組邊代替並且更優(一段長的邊明顯不如很多個比較短的邊)
於是用\(Floyd\)算出\(dis[u][v]\)之後列舉邊就能解決了。
一些結論:
利用這種方法求出剩餘的邊可能組成的不是一棵樹,比如邊長均為三的\(n=3\)的完全圖。
最短路樹只是對於一個確定節點\(u\),和一個任意節點\(v\),樹上兩點的路徑長度等於圖中兩點的最短路。所以\(u\)的最短路樹可以從\(u\)為起點\(run\)一遍\(dij\)求得。
程式碼:
#include<iostream> #include<cstring> #include<cmath> #include<cstdio> #include<algorithm> using namespace std; #define int long long const int N=310; const int INF=1e18; int dis[N][N],u[N*N],v[N*N],w[N*N]; signed main(){ int n,m; scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=INF; for(int i=1;i<=n;i++)dis[i][i]=0; for(int i=1;i<=m;i++){ scanf("%lld%lld%lld",&u[i],&v[i],&w[i]); dis[u[i]][v[i]]=dis[v[i]][u[i]]=min(dis[u[i]][v[i]],w[i]); } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[j][i]=dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); int ans=0; for(int i=1;i<=m;i++){ bool flag=0; for(int j=1;j<=n;j++){ if(v[i]==j||u[i]==j)continue; if(w[i]>=dis[u[i]][j]+dis[j][v[i]])flag=1; } if(flag)ans++; } printf("%lld",ans); return 0; }