题目大意:
给你一张图,有 k k k个点对 ( u i , v i ) (u_i,v_i) (ui,vi)。令 d ( i , j ) d(i,j) d(i,j)代表 i , j i,j i,j之间的最短路。
你现在可以使一条边的边权为0.问你最小的 ∑ i = 1 k d ( u i , v i ) \sum_{i=1}^{k}d(u_i,v_i) ∑i=1kd(ui,vi)
n , m , k ≤ 1000 n,m,k \leq 1000 n,m,k≤1000
题目思路:
先对每个点跑dij预处理出最短路矩阵: d i s t ( i , j ) dist(i,j) dist(i,j)
然后因为只是一条边 = 0.它是否能更新一个点对 ( u , v ) (u,v) (u,v)的最短路。就是求 一定经过该条边的最短路 是否比原本最短路更短
令这条边的两个点为 a , b a,b a,b.那么一定经过该点的最短路是 m i n ( d i s t [ u ] [ a ] + d i s t [ b ] [ v ] , d i s t [ u ] [ b ] + d i s t [ a ] [ v ] ) min(dist[u][a]+dist[b][v] , dist[u][b]+dist[a][v]) min(dist[u][a]+dist[b][v],dist[u][b]+dist[a][v]) (两种走法)
知道这个trick之后这题就没了
所以枚举边 = 0,然后点对最短路累加更新答案即可.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
const int maxn = 1005;
const int inf = 1e8;
int dp[maxn][maxn];
int n , m , k;
vector<pii> e[maxn];
vector<pii> Q;
struct Node
{
int id , dis;
bool operator < (const Node & a) const
{
return dis > a.dis;
}
};
void dij(int dist[] , int s)
{
priority_queue<Node> q;
int book[maxn];
for (int i = 1 ; i <= n ; i++){
dist[i] = inf;
book[i] = 0;
}
dist[s] = 0;
q.push({s , 0});
while (q.size()){
Node tmp = q.top();q.pop();
int id = tmp.id;
if (book[id]) continue;
book[id] = 1;
for (auto & g :e[id]){
int v = g.first , w = g.second;
if (dist[v] == -1 || dist[v] > dist[id] + w){
dist[v] = dist[id] + w;
q.push({v , dist[v]});
}
}
}
}
vector<pii> edge;
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> k;
for (int i = 1 ; i <= m ; i++){
int x , y , z; cin >> x >> y >> z;
e[x].pb (mp(y , z));
e[y].pb (mp(x , z));
edge.pb(mp(x , y));
}
for (int i = 1 ; i <= k ; i++){
int x , y; cin >> x >> y;
Q.pb(mp(x , y));
}
for (int i = 1 ; i <= n ; i++) dij(dp[i] , i);
int ans = 2e9;
for(auto p : edge){
int res = 0;
for (auto g : Q){
res += min (dp[g.first][g.second] ,
min(dp[g.first][p.first] + dp[p.second][g.second] ,
dp[g.first][p.second] + dp[p.first][g.second]));
}
ans = min (ans ,res);
}
cout << ans << endl;
return 0;
}