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