架设电话线

https://loj.ac/problem/10074

题目描述

  给出一张n个节点、m条边的加权无向图,求一条从1到n的路径,使第k+1大的边权尽可能小。

思路

  因为这个答案显然具有单调性,我们考虑对于这个答案进行二分,接下来再想如何确定这个值是否为所求。对于所有大于mid的边,我们把它的边权设为0,把所有小于等于mid的边的边权设为1,那么对于这张图,我们求一次最短路,得到的1~n的最短路径的值就是这个值再所求路径中的排名。或者换一种说法,对于小于等于mid的边权我们需要使用一次免费机会,看到n节点最少要用几次免费机会。

代码

#include <bits/stdc++.h>
using namespace std;
struct Edge
{
    int x,y,w;
}e[2005];
int nxt[4005],head[2005],to[4005],w[4005],dis[1005];
int tot,n,m,p,k;
bool exist[1005],f;
void add_edge(int x,int y,int v)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    w[tot]=v;
}
int spfa()
{
    memset(dis,0x3f,sizeof(dis));
    memset(exist,0,sizeof(exist));
    queue<int>q;
    q.push(1);exist[1]=1;dis[1]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[v]>dis[u]+w[i])
            {
                dis[v]=dis[u]+w[i];
                if(!exist[v])
                {
                    q.push(v);
                    exist[v]=1;
                }
            }
        }
        exist[u]=0;
    }
    if(dis[n]==0x3f3f3f3f)f=1;
    return dis[n];
}
bool check(int m)
{
    memset(head,0,sizeof(head));
    tot=0;
    for(int i=1;i<=p;i++)
    {
        int f=e[i].w<=m?0:1;
//        cout<<i<<' '<<f<<endl;
        add_edge(e[i].x,e[i].y,f);
        add_edge(e[i].y,e[i].x,f);
    }
    return spfa()<=k;
}
int main() 
{
    int maxx=0;
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=p;i++)
    {
        scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
        maxx=max(maxx,e[i].w);
    }
    int l=0,r=maxx;
    while(l<r)
    {
        if(f)break ;
        int mid=l+r>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    if(!f)printf("%d",r);
    else printf("-1");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fangbozhen/p/11680271.html