洛谷P1948Telephone Lines && poj3662 (二分+最短路)

题目链接:洛谷P1948[USACO08JAN]电话线Telephone Lines

分析:

题意:在无向图中找到一条路径,可以使k条边花费0,最后花费即为剩下的边中最大值,使这个最大值最小。

1.使最大值最小的问题明显可以二分一个mid。

2.由贪心可知,使k条花费为0必然是最大的k条边,即要求的值是路径中第k+1大的路径长度。

那么我们现在二分了一个值了,怎么check路径上是否有<=k条边小于mid呢?当然是规避比mid大的边走啦。

通过把>mid的边设成1,<=mid的边设成0,跑最短路

//洛谷P1948 
#include<bits/stdc++.h>
using namespace std;
#define N 1005
#define M 20005
int head[N],val[M],nex[M],to[M],w[M],dis[N],vis[N];
int tot=0,n,k;
void add(int a,int b,int ww){ to[++tot]=b; nex[tot]=head[a]; head[a]=tot; val[tot]=ww; }
struct pack{
    int s,dis;
};
priority_queue<pack> q;
bool operator < (const pack &a,const pack &b){
    return a.dis>b.dis;
}
int dij()
{
    memset(dis,0x7f7f7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    int s=1,t=n;
    dis[s]=0; q.push((pack){s,dis[s]});
    while(!q.empty()){
        pack t=q.top(); q.pop();
        int u=t.s;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=nex[i]){
            int v=to[i];
            if(dis[v]>dis[u]+w[i]){
                dis[v]=dis[u]+w[i];
                if(!vis[v]) q.push((pack){v,dis[v]});
            } 
        }
    }
    return dis[t];
}
bool check(int x)
{
    memset(w,0,sizeof(w));
    for(int i=1;i<=tot;i++) if(val[i]>x) w[i]=1;
    int diss=dij();
    if(diss<=k) return true;
    if(diss==2139062143) { printf("-1"); exit(0); }
    return false;
}
int main()
{
    int m,a,b,c,maxn=0,ans=0;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c); add(b,a,c);
        maxn=max(maxn,c);
    }
    int l=0,r=maxn;
    while(l<r){
        int mid=(l+r)>>1;
        if(!check(mid)) l=mid+1;
        else r=mid,ans=mid;
    }
    printf("%d
",ans);
    return 0;
}
/*
6 7 2
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
*/
原文地址:https://www.cnblogs.com/mowanying/p/11386657.html