1. 程式人生 > 其它 >AtCoder Beginner Contest 243 E - Edge Deletion( 刪除冗餘最短路)

AtCoder Beginner Contest 243 E - Edge Deletion( 刪除冗餘最短路)

傳送門
給出一個\(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; 
}