zoukankan      html  css  js  c++  java
  • 通往奥格瑞玛的道路

    通往奥格瑞玛的道路

    洛谷P1462 通往奥格瑞玛的道路

    题目简述

    本题的描述还是很清晰的,完全可以直接传送门去看

    但是唯一想要强调的是:初看本题可能会像我一样认为是点权+边权处理然后直接跑最短路...然鹅并不是这样


    思路分析

    我以为的点权其实是经过该城市的费用,是最终的答案

    我以为的边权其实是经过该条边的流血量,是用来判断某一条从(1)(N)的路径是否合法

    什么意思?

    就是要求我们找到一条路,满足:

    1. 这条路上所有的流血量之和必须<(b)(给定的初始血量)

    2. 这条路上经过的所有城市中最大费用值最小

    又是相当于同时维护两个东西,难搞

    之前有些题 中,我们会选择“枚举其中一个再求解另外一个的思路

    • 那这道题是否也能这样做呢?

    答案是肯定的,但是面对这道题的数据范围:(ci≤1000000000,fi≤1000000000)

    • 单纯的枚举肯定是会被T飞的,那我们不妨再深入思考一下:有什么算法或做法能够高效的代替穷举

    对了,那就是二分答案

    我们二分最大费用值最小,然后去(check)一下当前二分的答案是否合法:即是否有路径能够从(1)(N)且路径上城市花费值≤当前的答案

    • 那么问题来了,我们知道二分的前提条件即二分的数列必须是有序的,但是本题的数据并没有保证有序,怎么办?

    我们可以开另外一个数组(C')代替原来的城市费用数组(C)啊!然后直接对(C')进行(sort),那我们就可以对(C')这个有序数列进行二分了!

    • 当然,对于输出“AFK”的情况,我们可以在输入完后就跑一遍最短路,如果无法到达或路径不合法(流血总和≥(b))则直接“AFK”走人

    代码Code

    • 先上二分程序段:
    sort(cc+1,cc+1+n);  //变成有序序列
    ans=cc[n];
    l=1;r=n;
    while(l<=r) {  //注意一下这里二分的是下标并不是直接的值
         long long mid=(l+r)>>1;
         if(check(cc[mid])==true) {
            ans=cc[mid];
            r=mid-1;
         } 
         else l=mid+1;
    }
    printf("%lld",ans);
    
    
    • 再上(check)函数段:
    inline bool check(long long x) {
        for(register int i=1;i<=n;i++) flag[i]=0;  //记得清空!
        for(register int i=1;i<=n;i++) {
            if(c[i]>x)   flag[i]=1;  //所有大于当前答案的城市都不能走
        }
        dijkstra();
        if(dis[n]==1000000000+1||dis[n]>=b) return false;
        return true;
    }
    
    
    • 最后上完整AC程序:
    #include <bits/stdc++.h>
    using namespace std;
    long long b,w,r,ans,l,c[5100005],cc[5100005];
    int n,m,u,v,tot,dis[5100005],vis[5100005],flag[5100005];
    int head[5100005];
    priority_queue<pair<int,int> > shan;
    
    struct node {
    	int to,net;
    	long long val;
    } e[5100005];
    
    inline void add(int u,int v,int w) {
    	e[++tot].to=v;
    	e[tot].val=w;
    	e[tot].net=head[u];
    	head[u]=tot;
    }
    
    inline void dijkstra() {
    	for(register int i=1;i<=n;i++) {
    		vis[i]=0;
    		dis[i]=1000000000+1;
    	}
    	dis[1]=0;
    	shan.push(make_pair(0,1));
    	while(!shan.empty()) {
    		int x=shan.top().second;
    		shan.pop();
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=e[i].net) {
    			int v=e[i].to;
    			if(dis[v]>dis[x]+e[i].val&&flag[x]==0&&flag[v]==0) {
    				dis[v]=dis[x]+e[i].val;
    				shan.push(make_pair(-dis[v],v));
    			}
    		}
    	}
    }
    
    inline bool check(long long x) {
    	for(register int i=1;i<=n;i++) flag[i]=0;
    	for(register int i=1;i<=n;i++) {
    		if(c[i]>x)	flag[i]=1;
    	}
    	dijkstra();
    	if(dis[n]==1000000000+1||dis[n]>=b) return false;
    	return true;
    }
    
    int main() {
    	scanf("%d%d%lld",&n,&m,&b);
    	for(register int i=1;i<=n;i++) {
    		scanf("%lld",&c[i]);
    		cc[i]=c[i];
    	}
    	for(register int i=1;i<=m;i++) {
    		scanf("%d%d%lld",&u,&v,&w);
    		add(u,v,w);
    		add(v,u,w);
    	}
    	dijkstra();
    	if(dis[n]==1000000000+1||dis[n]>=b) {
    		puts("AFK");
    		return 0;
    	}
    	sort(cc+1,cc+1+n);
    	ans=cc[n];
    	l=1;r=n;
    	while(l<=r) {
    		long long mid=(l+r)>>1;
    		if(check(cc[mid])==true) {
    			ans=cc[mid];
    			r=mid-1;
    		}
    		else l=mid+1;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

  • 相关阅读:
    (转载)Hadoop示例程序WordCount详解
    Eclipse打不开,闪退
    【python】实例-把两个无规则的序列连接成一个序列,并删除重复的元素,新序列按照升序排序
    【python】格式化输出
    【python】序列、元组、集合、字典和range函数
    【python】实例-判断用户输入数字的类型
    【python】文件操作
    python 异常类型----后期需理解调整
    【python】OOP编程----类的定义及封装
    【python】if&&for&&while语句
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13207944.html
Copyright © 2011-2022 走看看