洛谷 通往奥格瑞玛的道路

点击就送屠龙宝刀

题目背景 Background
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量 有一天他醒来后发现自己居然到了联盟的主城暴风城 在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛
题目描述 Description
在艾泽拉斯,有n个城市。编号为1,2,3,…,n。
城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。
没经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。
假设1为暴风城,n为奥格瑞玛,而他的血量最多为b,出发时他的血量是满的。
歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。
输入输出格式 Input/output
输入格式:
第一行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。
输入输出样例 Sample input/output
输入样例
4 4 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
输出样例
10

刷了不少图论了23333
这道题有两种解题思路:搜索 or SPFA。
搜索:
数据范围是10000,搜索如果剪枝得当的话完全可以水过去。关键难点就在剪枝上。对于每一个点,我们用一个数组done维护这个点搜到N后所扣除的生命值和总收费,然后用一个use数组维护这个点是否被访问过。那么我们记录所有点扣除生命值的最大值和最小值。先从最大值的点开始搜索,如果这个点的血量不支持到达N那么这条路径全部剪除。在搜索时,只要满足总血量减去某个点的总扣除生命值那么剪除这条路径。这样复杂度会很客观。
图论:
开始时跑一边SPFA,记录最少消耗多少血量。如果最少消耗血量都不支持到达,那么直接输出“AFK”。若可以到达,有两种做法:第一种:对于每一个节点,建立一条指向自己的有向边,然后记录收费最少的路径(这题血量貌似没什么卵用。。),记录的同时判断是否满足血量大于0。当血量小于0时,回溯到上一步。那么我们就需要递归版SPFA了。
第二种做法:二分+SPFA。根据收费多少进行二分,那么我们一定至少会走收费少的那一边的一条路,然后跑SPFA,注意判断血量就行了。。。结尾再吐槽一句。。血量没用。。数据问题。。
还可以做做这道题→ 练习题

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

struct edge
{
    int to,next,w;
}e[100010];

int INF=0xfffffff;
int n,m,d;
int cost[10001];
int tot,head[10001];

void add(int a,int b,int c)
{
    e[tot].to=b;
    e[tot].next=head[a];
    e[tot].w=c;
    head[a]=tot++;
    e[tot].to=a;
    e[tot].next=head[b];
    e[tot].w=c;
    head[b]=tot++;
}

int dis[10001];

bool spfa(int x)
{
    int l=0,r=1;
    bool inq[10010]={};
    int q[10001]={};
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;q[0]=1;inq[1]=1;
    while(l!=r)
    {
        int now=q[l++];
        if(l==10001) l=0;
        for(int i=head[now];i;i=e[i].next)
        {
            if(cost[e[i].to]>x) continue;
            if(dis[e[i].to]>dis[now]+e[i].w)
            {
                dis[e[i].to]=dis[now]+e[i].w;
                if(!inq[e[i].to])
                {
                    q[r++]=e[i].to;
                    if(r==10001) r=0;
                    inq[e[i].to]=1;
                }
            }
        }
        inq[now]=0;
    }
    if(d-dis[n]<=0||dis[n]==INF) return 0;
    return 1;
}

int main()
{
    scanf("%d%d%d",&n,&m,&d);
    int i,ma=0;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        ma=max(ma,cost[i]);
    }
    for(i=1;i<=m;i++)
    {
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    add(a,b,c);
    }
    int ans;
    int l=0,r=ma,mid=(l+r)>>1;
    if(spfa(INF))
    {
    while(l<=r)
    {
    if(spfa(mid))
        {
            r=mid-1;
            ans=mid;
        }
            else l=mid+1;
            mid=(r+l)>>1;
            }
            printf("%d\n",ans);
    }
    else printf("AFK\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值