Description
给定一张图 ((n le 1000, m le 1000)),图上有 (k) 对点 ((a_i,b_i)) 表示有一个运输项目从 (a_i) 运输到 (b_i)。现在你可以选择一条边将它的权值变为零,要求最小化所有运输项目的最短距离的和。
Solution
考虑将每条边 (e(u,v)) 的权值变为 (0) 对结果的影响,对于运输计划 ((a,b)),其若不经过 ((u,v)) 则为其原答案,要么经过 ((u,v)) 则答案为 (min(d_{a,u}+d_{v,b}, d_{a,v}+d_{u,b})),二者取最小值即可。
于是我们只需要预处理出所有点对之间的最短路,然后模拟上述过程即可。求所有点对最短路的过程可以用 Dijkstra 做到 (O(nm log n))。
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
#define reset(x) memset(x,0,sizeof x)
#define reset3f(x) memset(x,0x3f,sizeof x)
namespace sp {
vector<pair<int,int> > g[N];
int n,v0=1,d[N],v[N];
void make(int t1,int t2,int t3) {
g[t1].push_back(make_pair(t2,t3));
}
void reset_graph() {
for(int i=0;i<=n;i++) g[i].clear();
}
void solve() {
priority_queue<pair<int,int> > qu;
reset3f(d);reset(v);
d[v0]=0;
qu.push(make_pair(0,v0));
while(qu.size()) {
int p=qu.top().second,r=qu.top().first;
qu.pop();
if(r+d[p]) continue;
for(int i=0;i<g[p].size();i++) {
int q=g[p][i].first,w=g[p][i].second;
if(d[q]>d[p]+w) {
d[q]=d[p]+w;
qu.push(make_pair(-d[q],q));
}
}
}
}
}
struct edge {int u,v,w;} ed[N];
int n,m,k,t1,t2,t3,a[N],b[N],d[N][N];
void readdata()
{
cin>>n>>m>>k;
for(int i=1;i<=m;i++)
{
cin>>t1>>t2>>t3;
sp::make(t1,t2,t3);
sp::make(t2,t1,t3);
ed[i]={t1,t2,t3};
}
for(int i=1;i<=k;i++)
{
cin>>a[i]>>b[i];
}
}
void presolve()
{
for(int i=1;i<=n;i++)
{
sp::v0=i;
sp::solve();
for(int j=1;j<=n;j++) d[i][j]=sp::d[j];
}
}
void solve()
{
int ans=1e18;
for(int i=1;i<=m;i++)
{
int u=ed[i].u,v=ed[i].v,w=ed[i].w;
int tans=0;
for(int j=1;j<=k;j++)
{
tans+=min(d[a[j]][b[j]],min(d[a[j]][u]+d[v][b[j]], d[a[j]][v]+d[u][b[j]]));
}
ans=min(ans,tans);
}
cout<<ans<<endl;
}
signed main()
{
readdata();
presolve();
solve();
return 0;
}