CodeForces 1307 D.Cow and Fields(最短路)

题意:

给一个n个点m条边的无向图,和k个特殊点
要你从k个特殊点选出两个点连边,问连边之后1到n的最短路距离最大是多少

思路:

先计算出:1到每个点的最短距离d[0][i]
点n到每个点的最短距离d[1][i]
假设选取的点是i,j,那么有两种情况:
1. 1->i->j->n,距离为d[0][i]+1+d[1][j]
2. 1->j->i->n,距离为d[0][j]+1+d[1][i]
连接i,j之后的最短路距离为他们的min

可以想到一种解法是枚举i和j,然后取max就是本题答案
但是复杂度是O(n^2),显然不可取,考虑如何优化

假设min是第一种情况,1->i->j->n
则d[0][i]+1+d[1][j]<=d[0][j]+1+d[1][i]
移项得d[0][i]-d[1][i]<=d[0][j]-d[1][j]
即i到1和n的最短路距离之差更小则i一定在j的前面
因此我们对于k个特殊点用d[0][k]-d[1][k]从小到大排序
这样就满足排序之后前面的点i一定在后面的点j之前
这样就可以O(n)枚举j了,满足条件的i是j之前的所有数
因为当前答案为d[0][i]+1+d[1][j]
对于j前面的所有数i的d[0][i]取个max再加上d[1][j]+1就是当前j的最大答案
对所有答案取max就是本题答案

ps:因为题目的边权都为1,所以可以直接用bfs计算最短路

code:

#include
using namespace std;
const int maxm=2e5+5;
vector<int>g[maxm];
int a[maxm];
int mark[maxm];
int d[2][maxm];//d[0][i]表示1到i的最短路距离,d[1][i]表示n到i的最短路距离
int n,m,k;
void bfs(int st,int id){
    queue<int>q;
    q.push(st);
    for(int i=1;i<=n;i++)mark[i]=0;
    mark[st]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int v:g[x]){
            if(mark[v])continue;
            d[id][v]=d[id][x]+1;
            q.push(v);
            mark[v]=1;
        }
    }
}
signed main(){
    cin>>n>>m>>k;
    for(int i=1;i<=k;i++){
        cin>>a[i];
    }
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    bfs(1,0);
    bfs(n,1);
    vector<pair<int,int> >temp;
    for(int i=1;i<=k;i++){
        temp.push_back({d[0][a[i]]-d[1][a[i]],d[1][a[i]]});
    }
    sort(temp.begin(),temp.end());
    int ans=0;
    int ma=0;
    for(int i=0;i<k;i++){
        if(i){
            ans=max(ans,ma+temp[i].second+1);
        }
        ma=max(ma,temp[i].first+temp[i].second);//键+值=d[0][i];
    }
    cout<<min(d[0][n],ans)<<endl;//还要和原图的最短路取个min
    return 0;
}

你可能感兴趣的:(CodeForces 1307 D.Cow and Fields(最短路))