[二分][spfa]通往奥格瑞玛的道路

6 篇文章 0 订阅
2 篇文章 0 订阅

题目

题目背景

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

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

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

题目描述

在艾泽拉斯,有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,可能有两条边连接着相同的城市。

题解

看到最大值最小基本上就是二分了
写写画画后发现是一个最短路的图
用了一些很骚的离散化和玄学初始化
经测验发现了一些很妙的卡常操作
1.数组比结构体快很多很多很多
2.memset很快,但是很多的多余memset不如for
3.int比bool快

ac代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#define N 10010
#define M 50010
#define INF 2147483647
using namespace std;
typedef long long LL;
queue<int>Q;
struct Node{
    int v,id;
}c[N];
struct Edge{
    int p,q,o,n;
}b[M*2];
int n,m,a[N],bo[N],t,h[N],num,p,q,o,l,r,mid,ans,d[N],w;
bool v[N];
bool cmp(Node p,Node q){
    return p.v<q.v;
}
void ljb(int p,int q,int o){
    b[++num].n=h[p];
    h[p]=num;
    b[num].p=p;
    b[num].q=q;
    b[num].o=o;
}
bool Spfa(){
    int x,y;
    if(v[1]||v[n])return 0;
    bo[1]=++t;
    Q.push(1);
    while(!Q.empty()){
        x=Q.front();
        Q.pop();
        bo[x]=0;
        for(int i=h[x];i!=0;i=b[i].n){
            y=b[i].q;
            if(!v[y]&&d[y]>d[x]+b[i].o){
                if(bo[y]!=t){
                    Q.push(y);
                    bo[y]=t;
                }
                d[y]=d[x]+b[i].o;
            }
        }
    }
//    cout<<t<<'\n';
//    for(int i=1;i<=n;i++)if(!v[i])cout<<i<<' '<<d[i]<<'\n';
    return d[n]<=w;
}
int main(){
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        c[i].v=a[i];
        c[i].id=i;
    }
    sort(c+1,c+n+1,cmp);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&p,&q,&o);
        if(p==q)continue;
        ljb(p,q,o);
        ljb(q,p,o);
    }
    l=1,r=n,ans=-1;
    for(int i=(l+r)/2+1;i<=r;i++)v[c[i].id]=1;
    while(l<r){
        mid=(l+r)/2;
//        memset(d,63,sizeof(d));
        for(int i=1;i<=mid;i++)d[c[i].id]=INF/2;
        d[1]=0;
        d[n]=INF/2;
        if(Spfa()){
            ans=c[mid].v;
            r=mid;
            for(int i=(l+r)/2+1;i<=r;i++)v[c[i].id]=1;
        }
        else {
            l=mid+1;
            for(int i=l;i<=(l+r)/2;i++)v[c[i].id]=0;
        }
    }
//    memset(d,63,sizeof(d));
    for(int i=1;i<=mid;i++)d[c[i].id]=INF/2;
    d[1]=0;
    d[n]=INF/2;
    if(Spfa())ans=c[l].v;
    if(ans==-1)printf("AFK\n");
    else printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值