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. 1-n的最短路不仅过a,b 最短路距离不变
  2. 经过a->b 最短路距离为d[1][a] + 1 + d[b][n]
  3. 经过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;
}
posted @ 2021-08-07 23:02  pxlsdz  阅读(46)  评论(0编辑  收藏  举报