AcWing 3797. 最大化最短路 BFS+贪心
题解
给定一个 n 个点 m 条边的无向连通图。
图中所有点的编号为 1∼n。
图中不含重边和自环。
指定图中的 k 个点为特殊点。
现在,你必须选择两个特殊点,并在这两个点之间增加一条边。
所选两点之间允许原本就存在边。
我们希望,在增边操作完成以后,点 1 到点 n 的最短距离尽可能大。
输出这个最短距离的最大可能值。
注意,图中所有边(包括新增边)的边长均为 1。
输入格式
第一行包含三个整数 n,m,k。
第二行包含 k 个整数 a1,a2,…,ak,表示 k 个特殊点的编号,ai 之间两两不同。
接下来 m 行,每行包含两个整数 x,y,表示点 x 和点 y 之间存在一条边。
输出格式
一个整数,表示最短距离的最大可能值。
数据范围
前六个测试点满足 2≤n≤100。
所有测试点满足 2≤n≤2×105,n−1≤m≤2×105,2≤k≤n,1≤ai≤n,1≤x,y≤n。
输入样例1:
5 5 3
1 3 5
1 2
2 3
3 4
3 5
2 4
输出样例1:
3
输入样例2:
5 4 2
2 4
1 2
2 3
3 4
4 5
输出样例2:
3
题解
本题有三种情况:
- 1-n的最短路不仅过a,b 最短路距离不变
- 经过a->b 最短路距离为d[1][a] + 1 + d[b][n]
- 经过b->a 最短路距离为d[1][b] + 1 + d[a][n]
本题要求加一条边使得最短路距离最大,增加一条边只会减少最短路距离,所以保持情况1是最好的情况,如果不能保持,那就取情况2,3最小值中最大的一个。也就是说min(情况1,min(max(情况2),max(情况3)),情况2和3,由多个特殊点控制,所以也会有诸多情况。
对应情况2和情况3,对于所有特殊点维护两个数组
- dist1:d[1][x] 1到x的距离
- dist2:d[x][n] x到n的距离
情况2需要找dist1[a] + 1 +dist2[b]最大值,情况3需要找dist1[b] + 1 +dist2[a]
如何高效找呢?
首先dist1[a] + 1 +dist2[b] <= dist1[b] + 1 +dist2[a]
dist1[a] - dist1[b] <= dist2[a] - dist2[b],
所以将特殊点其按照距离差值排序,可以保证第二种情况里的所有集合小于第三种,所以第三种直接出局
然后遍历一遍特殊点,对于特殊点每一个dist2[j]只需要dist1[1~j-1]的最大值相加即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 200010, M = N * 2;
int h[N], e[M], ne[M], idx;
int n, m, k;
int p[N], dist1[N], dist2[N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int q[N];
void bfs(int start, int dist[]){
memset(dist, 0x3f, N * 4);
int hh = 0, tt = -1;
q[++tt] = start;
dist[start] = 0;
while(hh<=tt){
int t = q[hh++];
for(int i = h[t]; ~i; i = ne[i]){
int j = e[i];
if(dist[j] > dist[t] + 1){
dist[j] = dist[t] + 1;
q[++tt] = j;
}
}
}
}
int main(){
memset(h, -1, sizeof(h));
scanf("%d%d%d", &n, &m, &k);
for(int i = 0; i < k; i++){
scanf("%d", &p[i]);
}
for(int i = 0; i < m; i++){
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
bfs(1, dist1);
bfs(n, dist2);
sort(p, p + k, [&](int x, int y){
return dist1[x] - dist1[y] < dist2[x] - dist2[y];
});
int maxx = dist1[p[0]], res = 0;
for(int i = 1; i < k; i++){
int t = p[i];
res = max(maxx + 1 + dist2[t], res);
maxx = max(dist1[t], maxx);
}
//cout<<(dist1[n])<<endl;
printf("%d", min(dist1[n], res));
return 0;
}