hdu6290 奢侈的旅行(优先队列优化的dijkstra)

12 篇文章 0 订阅

奢侈的旅行

Time Limit: 14000/7000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/Others)
Total Submission(s): 1918    Accepted Submission(s): 400


 

Problem Description

高玩小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

 

 

Source

"字节跳动杯"2018中国大学生程序设计竞赛-女生专场

解题思路

假设已经走了n条边,每条边对应a值为bi,那么当前的总花费为

log(1+b1) - log1 + log(1+b1+b2)-log(1+b1)...+log(1+b1+b2+...+bn) - log(1+b1+b2+...+bn-1)

=log(1+b1+b2+...+bn)

所以只要走过的路的a加起来最小,总花费就最小。且cost = log((level+ai)/level)=log(1+ai/level),所以level越小,对应的cost就越大,所以只要用a来当权值,用b来当限制条件求最短路就可以了。

代码如下

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <cmath>
#define maxn 100005
using namespace std;
typedef long long ll;
struct R{
    ll r, a, b;
    R(ll r, ll a,ll b): r(r), a(a), b(b){    }
};
vector<R> g[maxn];
ll dis[maxn];
ll get(ll x)
{
    ll k = 0;
    ll r = 1;
    while(r <= x){
        k ++;
        r *= 2;
    } 
    return k - 1;
}
struct node{
    ll x, w;
    node(ll x, ll w): x(x), w(w){    }
    bool operator<(const node& a)const{
        return w > a.w;
    }
};
int main()
{
    int T;
    scanf("%d", &T);
    while(T --){
        ll n, m;
        scanf("%lld%lld", &n, &m);
        for(int i = 1; i <= m; i ++){
            ll u, v, a, b;
            scanf("%lld%lld%lld%lld", &u, &v, &a, &b);
            g[u].push_back(R(v, a, b));
        }
        memset(dis, 0x3f, sizeof(dis));
        priority_queue<node> que;
        dis[1] = 1;
        bool flg = false;
        que.push(node(1, 1));
        while(!que.empty()){
            node top = que.top();
            que.pop();
            ll x = top.x;
            if(x == n){
                flg = true;
                break;
            }
            if(top.w > dis[x])
                continue;        
            for(int i = 0; i < g[x].size(); i ++){
                ll a = g[x][i].a;
                ll b = g[x][i].b;
                ll r = g[x][i].r;
                if(dis[x] + a < dis[r] && get(1 + a / dis[x]) >= b){
                    dis[r] = dis[x] + a;
                    que.push(node(r, dis[r]));
                }
            }
        }
        if(flg)
            printf("%lld\n", get(dis[n]));
        else
            puts("-1");
        for(int i = 1; i <= n; i ++)
            g[i].clear();
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值