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; 
} 
posted @ 2022-03-25 23:03  Xu-daxia  阅读(59)  评论(0编辑  收藏  举报