zoukankan      html  css  js  c++  java
  • HDU

    高玩小Q不仅喜欢玩寻宝游戏,还喜欢一款升级养成类游戏。在这个游戏的世界地图中一共有n个城镇,编号依次为1到n。

    这些城镇之间有m条单向道路,第i 条单项道路包含四个参数ui,vi,ai,bi,表示一条从ui号城镇出发,在vi号城镇结束的单向道路,因为是单向道路,这不意味着小Q可以从vi沿着该道路走到ui。小Q的初始等级level为1,每当试图经过一条道路时,需要支付cost=log2level+ailevel点积分,并且经过该道路后,小Q的等级会提升ai级,到达level+ai级。但是每条道路都会在一定意义上歧视低消费玩家,准确地说,如果该次所需积分cost < bi,那么小Q不能经过该次道路,也不能提升相应的等级。

    注意:本游戏中等级为正整数,但是积分可以是任意实数。

    小Q位于1号城镇,等级为1,现在为了做任务要到n号城镇去。这将会是一次奢侈的旅行,请写一个程序帮助小Q找到需要支付的总积分最少的一条路线,或判断这是不可能的。
    Input
    第一行包含一个正整数T(1≤T≤30),表示测试数据的组数。

    每组数据第一行包含两个整数n,m(2 ≤ n ≤ 100000,1 ≤ m ≤ 200000),表示城镇数和道路数。

    接下来m行,每行四个整数ui,vi,ai,bi(1≤ui,vi≤n,ui≠vi,0≤ai≤109,0≤bi≤60),分别表示每条单向道路。
    Output
    对于每组数据,输出一行一个整数,即最少所需的总积分的整数部分,如:4.9999输出4,1.0输出1。若不存在合法路线请输出−1。
    Sample Input
    1
    3 3
    1 2 3 2
    2 3 1 6
    1 3 5 0
    Sample Output
    2

    分析:首先这题肯定是个最短路没的说,重点是如何处理,因为有level的存在,看似其使得边权变成了动态的,对于一条边,固定的唯有一个ai值,还要对这个ai值做log2((leveli+a(i+1))/leveli)的处理才是真正的边权。但其实可以通过化简使题目多余的限制去掉。

    首先在当前点的等级设为level i ,在选择边时,有a[i+1]用于计算通过该边的花费。
    对于log2(n)对数,我们知道有,log2(x/y)=log2(x)-log2(y),因此,若当前从i- > j- > k

    原式计算花费cost为 log2((level[i] +a[j])/level[i]) + log2((level[j] +a[k])/level[j])
    转换为减法后 log2(level[i]+a[j]) - log2(level[i]) + log2(level[j]+a[k]) - log2(level[j])

    可以发现在行走过程中,level是如何变化的,是通过level[i]+a[j]得到了,也就是说,实际上level【i】+a【j】=level【j】。

    在上式中通过等价替换,log2(level[i]+a[j])与- log2(level[j])因为相等被抵消掉了,剩余 - log2(level[i]) + log2(level[j]+a[k]) 为i->k的花费

    在看,我们从起点到到达终点的总花费是如何计算的,即Σ(i=1~n) log2((level[i]+a[k])/leve[i]),其中,1~n指从起点出发到达终点的最短路径,而不是1~n所有节点。a[k]表示当前结点出发,下一条边的增加等级。转换为求和式即:
    原式= log2((level[1]+a[k1])/level[1]) + log2((level[2]+a[k2])/level[2]) + log2((level[3]+a[k3])/level[3]) + …………+log2((level[n-1]+a[kn-1])/level[n-1])

    根据上面的化简式,发现两项之间的分子和分母可以抵消掉。化简后得到:
    -log2(level[1])+log2(level[n-1]+a[kn-1])
    其中,题目已经说明,初始等级level为1级,那么前面那项 -log2(level[1])即值为0。也就是说,最终结果即log2(level[n-1]+a[kn-1])这一个式子。这个值拿去log2不谈,level[n-1]+a[kn-1]这个值,不就是从起点开始到终点n前的一个点的最小ai值之和吗,之后机上了a[kn-1]即达到终点那条边的最后一个a值,简单来说,即我们所要求的的最短路,其实和等级level毫无关系,全部被化简掉了,整整要求的最短路的边权,其实就是每条边上增长的等级ai,也就是说,把ai放到图上的边权中,裸的求一个最短路再对结果取log2即可。

    题目中还有一种限制,要求花费cost小于边上限制bi时,不需通过。我们已经把边权转换成了整数值,是不能再对其进行取log再与bi比较的,况且可能有精度损失。因此对于不等式 cost=log2((level+ai)/level ) < bi 对数逆运算,两边变为指数形式,即:
    (level[i] +a[j])/level[i] < 2^(bi)去掉了前式的log,直接以整数形式对比两值判断是否能通过某条边。

    最后注意一下,最短路dijkstra的过程中若没有用vis标记一下出队列的节点会超时。。。这是之前没做vis标记是从没出现过的情况orz也算是给自己做的警示了。

    AC代码

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int maxn=200005;
    const LL inf=1LL<<60;
    LL b2pow[70];
    int n,m,t;
    int head[maxn],cnt;
    struct edge
    {
        LL a;
        int to,b,next;
    }mp[maxn];
    void add(int from,int to,LL a,int b)
    {
        mp[++cnt].next=head[from];
        mp[cnt].to=to;
        mp[cnt].a=a;
        mp[cnt].b=b;
        head[from]=cnt;
    }
    struct Edge
    {
        LL dist;
        int to;
        Edge(int a,LL b)
        {
            to=a;
            dist=b;
        }
        bool operator <(const Edge &k)const
        {
            return dist>k.dist;
        }
    };
    void init()
    {
        b2pow[0]=1;
        for(int i=1;i<=60;i++) b2pow[i]=b2pow[i-1]<<1;
    }
    LL dist[maxn];
    void dijkstra(int s)
    {
        bool vis[maxn];
        for(int i=0;i<=n;i++) dist[i]=inf;
        for(int i=0;i<=n;i++) vis[i]=false;
        dist[s]=1;
        priority_queue<Edge>q;
        q.push(Edge(s,dist[s]));
        while(!q.empty())
        {
            Edge top=q.top();
            q.pop();
            if(vis[top.to])continue;
            vis[top.to]=true;
            for(int i=head[top.to];i!=0;i=mp[i].next)
            {
                edge tmp=mp[i];
                if(tmp.a/dist[top.to]+1<b2pow[tmp.b])continue;
                if(dist[tmp.to]>tmp.a+dist[top.to])
                {
                    dist[tmp.to]=tmp.a+dist[top.to];
                    q.push(Edge(tmp.to,dist[tmp.to]));
                }
            }
        }
    }
    int main()
    {
        init();
        scanf("%d",&t);
        while(t--)
        {
            int from,to,b;
            LL a;
            cnt=0;
            for(int i=0;i<=n;i++)head[i]=0;
            scanf("%d%d",&n,&m);
            for(int i=0;i<m;i++)
            {
                scanf("%d%d%lld%d",&from,&to,&a,&b);
                add(from,to,a,b);
            }
            dijkstra(1);
            printf("%.0f
    ",dist[n]==inf?-1:floor(log2(dist[n])));
        }
    }
    
  • 相关阅读:
    python中is和==的区别
    深拷贝和浅拷贝
    编码和解码
    with语句处理异常
    python中运行flask报错:UnicodeDecodeError: 'utf8' codec can't decodebyte 0xd5 in position 0:invalid continuation byte
    python中update的基本使用
    python中的程序控制结构
    python中的四种存储结构总结
    python中list,tuple,dict,set特点对比总结
    解决UIScrollview无故偏移和导航条遮挡view的问题
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135748.html
Copyright © 2011-2022 走看看