• P2502 [HAOI2006]旅行


    题目描述

    Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。Z小镇附近共有N个景点(编号为1,2,3,…,N),这些景点被M条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。速度变化太快使得游客们很不舒服,因此从一个景点前往另一个景点的时候,大家都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。

    输入输出格式

    输入格式:

    第一行包含两个正整数,N和M。

    接下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。

    最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

    输出格式:

    如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。

    输入输出样例

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

    说明

    【数据范围】

    1<N≤500

    1≤x,y≤N,0<v<30000,x≠y

    0<M≤5000

    Solution:

      一道很值得做的题目。。。(早上$5:40$起床,就为打这题(`~`))

      开始的思路是边权那么小,所以从小到大枚举最小边权,然后跑$spfa$求出从$st$达到$ed$的最大边权的最小值,然后更新比较就好了。

      以为很完美的思路,从$6:00$码到$7:20$样例死活过不了,调试了好久,最后发现这个方法漏洞很多。首先因为每次枚举完最小边权后,要求最大边权的最小值,那么$spfa$每次就必须重复入队,问题是无向图有环,于是我又限制入队次数,后面发现这样重复入队会造成多次更新,然后答案就不准了,后面搞了很多骚操作,尼码~终于过了$2$个样例,但是这样多次入队, 常数极其大,时间复杂度撑不过去。(挂惨~挨饿去抗早自习~)

      上课时,神游中思考了一会,发现我是真T*D的蠢,这道题等价于直接判断起点终点是否连通,求最大边权和最小边权的最小比。

      那么直接搞一遍最小生成树不就$OK$了嘛~

      中午,码了一会儿,从$80 ightarrow 80 ightarrow 100$。

      思路还是枚举最小边权,然后直接判断图的连通性。先将边权从小到大排序,然后每次枚举完最小边权$k$,二分找出等于$k$的第一条边,若等于$k$的边不存在则继续枚举(可行性剪枝),若存在则每次往图中加边,判断$fa[st]$是否等于$fa[ed]$,相等时就更新一下答案,跳出本次循环继续枚举$k$(最优性剪枝,往后显然答案更大)。

      代码写的太繁琐了,于是简化了一下,最后时间复杂度比较玄学,最坏情况下就是每条边边权都不想等,此时应该为$O(frac{m^2*logm}{2})$(貌似也有一亿多点,数据比较水没有故意卡)。

      (蒟蒻后面默默的发现,竟然有人用$spfa;A;$了这道题,还是最优解,为毛我写不出,啊啊~!)

    代码:

    #include<bits/stdc++.h>
    #define il inline
    #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define Min(a,b) ((a)>(b)?(b):(a))
    #define Max(a,b) ((a)>(b)?(a):(b))
    using namespace std;
    
    const int N=100005;
    int n,m,st,ed,fa[5005],cnt,ans1=30005,ans2=1,mx;
    bool vis[N];
    struct node{
        int to,fr,w;
        bool operator<(const node a)const{return w<a.w;}
    }e[N];
    
    il int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9')x=getchar();
        while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
        return a;
    }
    
    il int gcd(int a,int b){return b?gcd(b,a%b):a;}
    
    il int find(int x){return x==fa[x]?fa[x]:find(fa[x]);}
    
    int main(){
        n=gi(),m=gi();
        int u,v,w;
        For(i,1,m)e[i].fr=gi(),e[i].to=gi(),e[i].w=gi(),mx=max(mx,e[i].w);
        For(i,1,n)fa[i]=i;
        st=gi(),ed=gi();
        sort(e+1,e+m+1);
        For(k,1,mx){
            int l=1,r=m,mid,pos=0;
            while(l<=r){
                mid=l+r>>1;
                if(e[mid].w<k)l=mid+1,pos=mid;
                else r=mid-1;
            }
            pos++;
            if(e[pos].w==k){
                For(i,1,n)fa[i]=i;
                For(i,pos,m)
                    if(find(ed)!=find(st)){
                        int x=find(fa[e[i].fr]),y=find(fa[e[i].to]);
                        if(x!=y)fa[y]=x;
                        if(find(fa[ed])==find(fa[st])){
                            if(e[i].w*ans2<k*ans1)ans1=e[i].w,ans2=k;
                            break;
                        }
                    }
            }
        }
        if(ans1>30000){cout<<"IMPOSSIBLE";return 0;}
        int p=gcd(ans1,ans2);
        if(ans1%ans2)cout<<ans1/p<<'/'<<ans2/p;
        else cout<<ans1/ans2;
        return 0;
    }
  • 相关阅读:
    Linux 线程间通信方式+进程通信方式 总结
    使用opencv第三方库的makefile文件示例
    rplidar SDK 二次开发---之获取目标信息(0.1)
    #include "Target_orientation.h"
    opencv —— 调用摄像头采集图像 VideoCapture capture(0);
    cmake 支持-lpthread
    ROS下sensor_msgs::ImagePtr到sensor_msgs::Image之间的转换
    JAVA 校验身份证号码工具类(支持15位和18位)
    python面向对象游戏练习:好人坏人手枪手榴弹
    python 私有属性的作用
  • 原文地址:https://www.cnblogs.com/five20/p/9110370.html
Copyright © 2020-2023  润新知