洛谷 P1948 [USACO08JAN]Telephone Lines S(贪心+最短路)

题目链接:https://www.luogu.com.cn/problem/P1948

 

这道题的题意大体是:求原点1到n的所有路中的第k+1长的路最小。

这道题的思路比较好:

首先,是最大值最小的问题,会用到二分答案。

其次,要将前k大的路免费掉,那么可以将大于二分值的路径长度设为1,将小于二分值的路径长度设为0,这样等于跑一边0/1最短路,如果dis[n]<=k,那说明在限制条件之内,可以继续缩小二分范围。否则说明无法满足。

 

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=10005;
queue<int> q;
struct node{
    int to,next,w;
}edge[N<<1];
int head[N],dis[N],vis[N];
int n,p,m,tot,k; 
void add(int u,int v,int w){
    edge[tot].next=head[u];
    edge[tot].to=v;
    edge[tot].w=w;
    head[u]=tot++;
}
bool check(int u,int v){
    return u>v;
}
bool spfa(int x){
    memset(dis,0x3f3f,sizeof(dis));
    while(!q.empty()) q.pop();
    q.push(1); dis[1]=0; vis[1]=1;
    while(!q.empty()){
        int u=q.front(); q.pop(); vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(dis[v]>dis[u]+check(edge[i].w,x)){
                dis[v]=dis[u]+check(edge[i].w,x);
                if(!vis[v]) q.push(v),vis[v]=1;
            }
        }
    }
    if(dis[n]<=k) return 1;
    return 0;
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=p;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w); add(v,u,w);
    }
    //printf("-1");
    int ans=-1;
    int l=0,r=1000000;
    while(l<=r){
        int m=(l+r)>>1;
        //printf("-1");
        if(spfa(m)) {ans=m; r=m-1;}
        else l=m+1;
    }
    printf("%d",ans);
    return 0;
}
AC代码

 

posted @ 2020-08-06 22:37  dfydn  阅读(121)  评论(0编辑  收藏  举报