zoukankan      html  css  js  c++  java
  • P1462 通往奥格瑞玛的道路[最短路+二分+堆优化]

    题目来源:洛谷

    题目背景

    在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量

    有一天他醒来后发现自己居然到了联盟的主城暴风城

    在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛

    题目描述

    在艾泽拉斯,有n个城市。编号为1,2,3,...,n。

    城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

    每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

    假设1为暴风城,n为奥格瑞玛,而他的血量最多为b,出发时他的血量是满的。

    歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

    输入输出格式

    输入格式:

    第一行3个正整数,n,m,b。分别表示有n个城市,m条公路,歪嘴哦的血量为b。

    接下来有n行,每行1个正整数,fi。表示经过城市i,需要交费fi元。

    再接下来有m行,每行3个正整数,ai,bi,ci(1<=ai,bi<=n)。表示城市ai和城市bi之间有一条公路,如果从城市ai到城市bi,或者从城市bi到城市ai,会损失ci的血量。

    输出格式:

    仅一个整数,表示歪嘴哦交费最多的一次的最小值。

    如果他无法到达奥格瑞玛,输出AFK。

    输入输出样例

    输入样例#1: 
    4 4 8
    8
    5
    6
    10
    2 1 2
    2 4 1
    1 3 4
    3 4 3
    
    输出样例#1: 
    10
    

    说明

    对于60%的数据,满足n≤200,m≤10000,b≤200

    对于100%的数据,满足n≤10000,m≤50000,b≤1000000000

    对于100%的数据,满足ci≤1000000000,fi≤1000000000,可能有两条边连接着相同的城市。(注意细节)

    这道题值得一写啊。。。我卡了差不多十次。每次都是一点点瑕疵。

    一开始想着dfs暴力搞定,如你所料,TLE了。。。于是转向求助于dijkstra,看了看书发现最短路可以做这道题,结果是最后骗AFK骗到45分。。。

    无奈求助于题解,遂用二分解之。

    我解释的挺不清楚的,因为我自己也没有理解透彻,这道题比较绕,容易把最大跟最小还有限制条件一下子搞混了,就全乱了。


    解析:

    题目有点难理解,先解释一下题目:

    对于一个有向图G,有两种信息:一个是每条边的权,一个是每个点的费用。

    对于这个给定的有向图G,从1到n的通路是一定的。

    限制条件:给定一个值,使得某条1到n的通路的边权之和不大于这个值。

    我们假设满足以上限制的这些1到n的通路的集合为S(n)。

    对于这个集合S中的每个元素(即每条通路),我们记该路径上点的费用的最大值为另一个集合max(n)。

    嘿,有了上面这些数学语言的描述作基础,我们可以很简单的理解题意。

    我们就是要求出max集合中的最小值。

    思路大概是这样:

    我们首先要得到合法的可达的通路,使得歪嘴哦不死,然后就是对这条通路做限制,使得这条通路对应的max尽可能小。

    我们知道,可选的点是一定的,所以本题的答案一定在所有点的集合中,为了得到更小的让歪嘴哦不死的一个max,我们有一个解法。

    假设所有可选的点是a1,a2,...an,我们就把它们排个序,从大到小来找一个既使得歪嘴哦不死,又使得max尽量小的点费用。

    因为要让他尽可能不死,所以我们用最短路算法来尽量让他活下来。

    哎,发现了没有,“从大到小来找一个既使得歪嘴哦不死,又使得max尽量小的点费用。”这样一个设定,使得我们可以用二分去解它!

    二分的前提是,歪嘴哦不死!

    我用了一个dij来判断歪嘴哦在当前点费用限制下会不会死:

     1 bool dijkstra(int limit)
     2 {
     3     memset(v,0,sizeof(v));
     4     memset(d,0x3f,sizeof(d));
     5     d[1]=0;
     6     q.push(make_pair(0,1));
     7     while(q.size()){
     8         int index=q.top().second;q.pop();
     9         if(v[index]) continue;
    10         v[index]=1;
    11         for(int i=head[index];i;i=g[i].next){
    12             int y=g[i].ver,z=g[i].edge;
    13             if(d[y]>d[index]+z&&c[y]<=limit){
    14                 d[y]=d[index]+z;
    15                 q.push(make_pair(d[y],y));
    16             }
    17         }
    18     }
    19     if(d[n]<=hp) return 1;
    20     else return 0;
    21 }

    这个limit就是我们对这条最短路能走过的点费用最大值的限制。

    然后是这个二分:

     1 sort(u+1,u+n+1);
     2     int l=1,r=n,ans=c[n];
     3     while(l<=r){
     4         int mid=(l+r)>>1;
     5         if(dijkstra(u[mid]))
     6         {
     7             r=mid-1;ans=u[mid];
     8         }
     9         else l=mid+1;
    10     }
    11     printf("%d
    ",ans);

    不做任何关于点费用的限制歪嘴哦都会死的话,那就只能AFK了(他真惨):

    1   if(!dijkstra(INF)){
    2         printf("AFK
    ");return 0;
    3     }

    参考代码:

    具体的我就不写注释了,上面已经写的很清楚了。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<cstring>
    #define N 10010
    #define INF 0x3fffffff
    using namespace std;
    int head[N],d[N*100],tot,n,m,hp;
    int u[N*100],c[N*100];
    bool v[N*100];
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    struct node{
        int ver,next,edge;
    }g[N*100];
    void add(int x,int y,int val)
    {
        g[++tot].ver=y,g[tot].edge=val;
        g[tot].next=head[x],head[x]=tot;
    }
    
    bool dijkstra(int limit)
    {
        memset(v,0,sizeof(v));
        memset(d,0x3f,sizeof(d));
        d[1]=0;
        q.push(make_pair(0,1));
        while(q.size()){
            int index=q.top().second;q.pop();
            if(v[index]) continue;
            v[index]=1;
            for(int i=head[index];i;i=g[i].next){
                int y=g[i].ver,z=g[i].edge;
                if(d[y]>d[index]+z&&c[y]<=limit){
                    d[y]=d[index]+z;
                    q.push(make_pair(d[y],y));
                }
            }
        }
        if(d[n]<=hp) return 1;
        else return 0;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&hp);
        for(int i=1;i<=n;i++){
            scanf("%d",&c[i]);//到达每个点的费用 
            u[i]=c[i];//我们额外搞一个数组来二分
        }
        for(int i=1;i<=m;i++){
            int x,y,val;
            scanf("%d%d%d",&x,&y,&val);//过边损失血量 
            if(x==y) continue;
            add(x,y,val);add(y,x,val);
        }
        if(!dijkstra(INF)){
            printf("AFK
    ");return 0;
        }
        sort(u+1,u+n+1);
        int l=1,r=n,ans=c[n];
        while(l<=r){
            int mid=(l+r)>>1;
            if(dijkstra(u[mid]))
            {
                r=mid-1;ans=u[mid];
            }
            else l=mid+1;
        }
        printf("%d
    ",ans);
        return 0;
    }

    2019-05-31 19:07:02

  • 相关阅读:
    [BZOJ2038]小Z的袜子
    [BZOJ5016]一个简单的询问
    [BZOJ1008][HNOI2008]越狱
    [FZU2254]英语考试
    利用Map 的merge方法统计数量
    List 原生态类型
    try-with-resource 关闭 io流
    利用构建器创建对象
    linux 安装 vault
    git 上传文件
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/10951142.html
Copyright © 2011-2022 走看看