首页 > 其他 > 详细

洛谷P1462 通往奥格瑞玛的道路(SPFA+二分答案)

时间:2019-11-05 22:37:17      阅读:97      评论:0      收藏:0      [点我收藏+]

题目背景

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

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

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

题目描述

在艾泽拉斯,有\(n\)个城市。编号为\(1,2,3,\dots,n\)

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

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

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

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

输入格式

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

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

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

输出格式

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

如果他无法到达奥格瑞玛,输出\(\text{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 \le 200,m \le 10^4,b \le 200\)

对于\(100%\)的数据,满足\(n \le 10^4\)\(m \le 5 \times 10^4\)\(b \le 10^9\),满足\(c_i \le10^9,f_i \le 10^9\)可能有两条边连接着相同的城市。

血量为0的时候,视为可以通过.


解题报告

题意理解

这道题目,让我们满足两个条件.

  1. 血量充足,也就是路径消耗要小于血量最大值.
  2. 这条路径上经过的点,最大点权最小.

  3. 从起点到终点


算法解析

如果说排除,血量因素,这道题目是非常显然的最短路径问题.

但是我们现在多了一个限制条件,那么该如何是好?

我们知道,如果说我们的最终答案\(x_1\),也就是说答案路径上最大点的点权\(x_1\).

现在有一个值\(x_2\),表示限制目标答案路径上最大点的点权为\(x_2\)
\[ 若x_1<x_2,显然x_2这个条件是可以满足的 \]
同理,我们也可以推断出.
\[ 若x_1>x_2,显然x_2这个条件是无法满足的,因为题目的答案是x_1,而不是x_2. \]
综上所述,我们不难得出结论.

这道题目可以,二分最大点权,也就是二分答案

既然如此,我们的判断\(check\)函数,如何处理呢?

我们可以用最短路算法作为判断.以血量作为权值.

转移,则是满足.这条边抵达的终点\(y\),需要满足\(y<=mid\)

然后这道题目就这样解决了.


代码解析

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+20,M=5e4+20;
#define int long long//这道题目数据范围有点大,所以为了抱歉,我们选择long long
int n,m,b,f[N],l,r,head[M],edge[M<<1],Next[M<<1],ver[M<<1],tot,vis[N],dis[N];
inline int Spfa(int mid)
{
    memset(vis,false,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    queue<int> q;
    q.push(1);
    dis[1]=0;
    if (f[1]>mid)//初始点也要判断
        return false;
    while(q.size())
    {
        int now=q.front();
        q.pop();
        vis[now]=false;
        for(int i=head[now]; i; i=Next[i])
        {
            int y=edge[i],w=dis[now]+ver[i];
            if (dis[y]>w && f[y]<=mid)//目标点点权不可以大于限制值
            {
                dis[y]=w;
                if (!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    return dis[n]<=b;//需要满足血量花费不可以大于最大花费
}
inline void add_edge(int a,int b,int c)
{
    edge[++tot]=b;
    ver[tot]=c;
    Next[tot]=head[a];
    head[a]=tot;
}
inline void init()
{
    scanf("%lld%lld%lld",&n,&m,&b);
    for(int i=1; i<=n; i++)
    {
        scanf("%lld",&f[i]);
        r=max(f[i],r);//最大点权
    }
    for(int i=1; i<=m; i++)
    {
        int a,b,c;
        scanf("%lld%lld%lld",&a,&b,&c);
        add_edge(a,b,c);
        add_edge(b,a,c);//建立无向图
    }
    while(l<r)//二分
    {
        int mid=l+r>>1;
        if (Spfa(mid))
            r=mid;
        else
            l=mid+1;
    }
    if (!Spfa(r))//无解
        puts("AFK");
    else
        printf("%lld\n",r);
}
signed main()
{
    init();
    return 0;
}

洛谷P1462 通往奥格瑞玛的道路(SPFA+二分答案)

原文:https://www.cnblogs.com/gzh-red/p/11801740.html

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