AcWing340通信线路(dijkstra+二分)(分层图最短路)

题目地址https://www.acwing.com/problem/content/342/

题目描述

在郊区有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站AiiBi。

特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。

现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费Li。

电话公司正在举行优惠活动。

农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。

农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。

求至少用多少钱可以完成升级。

输入格式

第1行:三个整数N,P,K。

第2..P+1行:第 i+1 行包含三个整数Ai,Bi,Li。

输出格式

包含一个整数表示最少花费。

若1号基站与N号基站之间不存在路径,则输出”-1”。

数据范围

0K<N1000
1P10000
1Li1000000

题解:这道题可以用二分+单源最短路来解决,而且用二分非常巧妙。枚举答案[0,1e6+1],至于这里的答案区间为什么是这样的,后面再解释。假设当前二分的是x,那么便可以求1的单源最短路,并且将边的权值大于x的边的权值看作为1,小于等于x的边的权值看作为0,那么求出的dis[n]就是在答案为x时,最小需要不用付出代价的边的数量,当dis[n]<=k,则x可以满足,那么就可以继续枚举x的左边,否则枚举x的右边。所以最后求出的是最小的一个x使得dis[n]<=k.至于答案区间[0,1e6+10],0是因为可能会出现k大于等于1到n之间的最少的边的数量,1e6+1,因为可能会出现1到n之间并不相通,那么最后的二分答案就是1e+10,然后就直接输出-1

AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int N=1010,M=1e4+10;
typedef pair<int,int> P;
struct node{
    int from,to,dis,next;
}edge[M*2];
int head[N],cnt=0;

void addedge(int u,int v,int w){
    cnt++;
    edge[cnt].from=u;
    edge[cnt].to=v;
    edge[cnt].dis=w;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

bool dijkstra(int x,int n,int k){
    int dis[n+10],p[n+10]={0};
    memset(dis,0x3f,sizeof(dis));
    priority_queue<P,vector<P>,greater<P> > q;
    q.push(make_pair(0,1));
    dis[1]=0;
    while(!q.empty()){
        P now=q.top();q.pop();
        int u=now.second,s=now.first,w;
        if(p[u]) continue;
        p[u]=1;
        for(int i=head[u];~i;i=edge[i].next){
            if(edge[i].dis>x) w=1;
            else w=0;
            if(dis[edge[i].to]>dis[edge[i].from]+w){
                dis[edge[i].to]=dis[edge[i].from]+w;
                q.push(make_pair(dis[edge[i].to],edge[i].to));
            }
        }
    }
    if(dis[n]>k) return true;
    else return false;
}

int main(){
    int n,m,k;cin>>n>>m>>k;
    int u,v,w;
    memset(head,-1,sizeof(head));
    while(m--){
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    int l=0,r=1e6+1,mid;
    while(l<r){
        mid=l+r>>1;
        if(dijkstra(mid,n,k)) l=mid+1;
        else r=mid;
    }
    if(r==1e6+1) cout<<"-1";
    else cout<<r;
    return 0;
}

题解:这道题也可以直接使用分层图最短路来解决

AC代码

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1010,M=1e4+10;
struct st{
    int from,to,dis,next;
}edge[M*2];
int head[M*2],cnt=0,dis[N][N];
struct node{
    int j,s,i;
    node(int s,int i,int j):s(s),i(i),j(j){};
    friend bool operator>(node a,node b){
        return a.s>b.s;
    }
};

void addedge(int u,int v,int w){
    cnt++;
    edge[cnt].from=u;
    edge[cnt].to=v;
    edge[cnt].dis=w;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

void spfa(int s,int k){
    memset(dis,0x3f,sizeof(dis));
    priority_queue<node,vector<node>,greater<node> >q;
    q.push(node(0,s,0));
    dis[0][s]=0;
    while(!q.empty()){
        node now=q.top();q.pop();
        int u=now.i,j=now.j;
        for(int i=head[u];~i;i=edge[i].next){
            if(dis[j][edge[i].to]>max(dis[j][edge[i].from],edge[i].dis)){
                dis[j][edge[i].to]=max(dis[j][edge[i].from],edge[i].dis);
                q.push(node(dis[j][edge[i].to],edge[i].to,j));
            }
            if(j<k&&dis[j+1][edge[i].to]>dis[j][u]){
                dis[j+1][edge[i].to]=dis[j][u];
                q.push(node(dis[j+1][edge[i].to],edge[i].to,j+1));
            }
        }
    }
}

int main(){
    int n,m,k;cin>>n>>m>>k;
    int u,v,w;
    memset(head,-1,sizeof(head));
    while(m--){
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    spfa(1,k);
    int minn=0x3f3f3f3f;
    for(int i=0;i<=k;i++){
        minn=min(minn,dis[i][n]);
    }
    if(minn==0x3f3f3f3f) cout<<"-1";
    else cout<<minn;
    return 0;
}

写于:2020/9/5 16:51

 

posted @ 2020-09-05 16:52  白菜茄子  阅读(246)  评论(0编辑  收藏  举报