首页 > 其他 > 详细

P1948 [USACO08JAN]电话线Telephone Lines

时间:2019-08-20 20:37:09      阅读:109      评论:0      收藏:0      [点我收藏+]

原题链接  https://www.luogu.org/problem/P1948

技术分享图片

技术分享图片

简化题意:

给你一个无向图,让你去掉 k 条边后求 1~n 所经过路径中的最大边最小是多少 。

解题思路:

看到这个问法,是二分没错了!关键是怎么二分 。

按照一般的套路,我们直接二分答案,那么这里就二分这个最大边!

既然我们二分的这条边是最小的最大边,也就是说不会再经过任何比这条边还大的边了;

为了防止经过那些比它大的边,我们可以利用那 k 次免费机会 。

一个炒鸡敲庙的思路:

我们将所有比我们二分出来的这条边大的边的值暂时赋成 1,其余的赋成 0(代码里用 now 表示),我们从点 1 跑一次最短路,那么 dis [ n ] 就是从 1 到 n 所花费的最少免费次数 。

判断我们二分的这一条边是否合法,就可以看看 dis [ n ] 和 k 的大小关系:

如果 dis [ n ] <= k,说明这是一个合法的方案,我们可以继续往小里二分;否则要往大里二分!

其实也不是很难嘛qwq

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int read()
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<0||ch>9)
    {
        if(ch==-) x=-x;
        ch=getchar();
    }
    while(ch>=0&&ch<=9)
    {
        a=(a<<1)+(a<<3)+(ch-0);
        ch=getchar();
    }
    return a*x;
}
const int inf=1e9;
int n,m,k,l,r,ans,edge_sum;
int u[10001],v[10001],w[10001],dis[10001],vis[10001],head[10001];
struct node
{
    int now,dis,from,to,next;                //now是存这条边和我们二分的mid的大小关系的,不懂的往下看看就好了 
}a[100001];
void add(int from,int to,int dis)            //链表建边 
{
    edge_sum++;
    a[edge_sum].from=from;
    a[edge_sum].to=to;
    a[edge_sum].dis=dis;
    a[edge_sum].next=head[from];
    head[from]=edge_sum;
}
bool check(int x)                            //SPFA判断合法性 
{
    queue<int> q;
    for(int i=1;i<=n;i++)                    //注意初始化 
    {
        dis[i]=inf;
        vis[i]=0;
    }
    for(int i=1;i<=edge_sum;i++)
    {
        if(a[i].dis>x) a[i].now=1;           //把比mid大的边设为1 
        else a[i].now=0;                     //其他的设为0 
    }
    dis[1]=0;
    q.push(1);
    vis[1]=0;
    while(!q.empty())         
    {
        int f=q.front();
        q.pop();
        vis[f]=0;
        for(int i=head[f];i;i=a[i].next)
        {
            int zd=a[i].to;
            if(dis[zd]>dis[f]+a[i].now)      //注意这里用到的是now 
            {
                dis[zd]=dis[f]+a[i].now;
                if(!vis[zd])
                {
                    vis[zd]=1;
                    q.push(zd);
                }
            }
        }
    }
    if(dis[n]<=k) return 1;                  //看看用的免费次数是否小于k 
    else return 0;
}
int main()
{
    n=read();m=read();k=read();              //n个点,m条边,k次免费机会 
    l=1e9;
    for(int i=1;i<=m;i++)                  
    {
        u[i]=read();v[i]=read();w[i]=read();
        add(u[i],v[i],w[i]);                 //注意建双向边 
        add(v[i],u[i],w[i]);
        l=min(l,w[i]);                       //找二分枚举的上下界 
        r=max(r,w[i]);
    }
    while(l<=r)                              //二分答案 
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    if(ans==0) printf("-1");                 //如果没有找到答案,就是无解 
    else printf("%d",ans);
    return 0;
}

 

P1948 [USACO08JAN]电话线Telephone Lines

原文:https://www.cnblogs.com/xcg123/p/11385403.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!