In Touch HDU - 5361(优先队列优化dijkstra+并查集优化)

In Touch

HDU - 5361

There are n soda living in a straight line. soda are numbered by 1,2,,n from left to right. The distance between two adjacent soda is 1 meter. Every soda has a teleporter. The teleporter of i-th soda can teleport to the soda whose distance between i-th soda is no less than li and no larger than ri. The cost to use i-th soda's teleporter is ci.

The 1-st soda is their leader and he wants to know the minimum cost needed to reach i-th soda (1in)

.
Input There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1n2×105), the number of soda.
The second line contains n integers l1,l2,,ln. The third line contains n integers r1,r2,,rn. The fourth line contains n integers c1,c2,,cn. (0lirin,1ci109)
Output For each case, output n integers where i-th integer denotes the minimum cost needed to reach i-th soda. If 1-st soda cannot reach i-the soda, you should just output -1. Sample Input
1
5
2 0 0 0 1
3 1 1 0 5
1 1 1 1 1
Sample Output
0 2 1 1 -1

        
  
Hint
If you need a larger stack size, 
please use #pragma comment(linker, "/STACK:102400000,102400000") and submit your solution using C++.
        
 

题意:有n个点站成一排,相邻距离为1,每个点 i 可以联系上距离自己 x 的点并且花费Ci,其中Li<=x<=Ri,从点1开始,求联系到每个点的最少费用。

思路:边太多,不可能建完边后再求最短路,感觉有点像隐式图,然后就是巧妙用到Dijstra,需要注意到的就是,这里是每个点有权值而不是边,那么dist[i]表示从1到 i 的花费再加上点 i 的花费,这样每个点就只会被更新一次,更新后在以后就不会再次被更新了,这里用到并查集把已经更新的点得father指向还没被更新的点。

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<ll,int> Pir;
const ll INF = 1LL << 60;
const int maxn = 2*1e5+10;
ll L[maxn],R[maxn],C[maxn],dis[maxn];
int n,pre[maxn];
void init(){
    for(int i = 0; i <= n+5; i++){
        pre[i] = i;
        dis[i] = INF;
    }
}
int Find(int x){
    if(x == pre[x])
        return x;
    else return pre[x] = Find(pre[x]);
}
void solve(){
    dis[1] = C[1];
    priority_queue<Pir,vector<Pir>,greater<Pir> > q;
    q.push(make_pair(dis[1],1));
    while(!q.empty()){
        Pir st = q.top();//相当于选出最小的作为起点
        q.pop();
        int u = st.second;
        for(int i = -1; i <= 1; i += 2){
            int l = u + i * L[u];
            int r = u + i * R[u];
            if(l > r) swap(l,r);
            l = max(1,l);
            l = min(n+1,l);
            if(l > r) continue;//确定合理距离范围
            for(int v = l;;v++){
                v = Find(v);//直接指向未更新的点
                if(v < 0 || v > n || v > r) break;
                if(dis[v] > dis[u] + C[v]){//每次都加上当前点需要的花费。
                    dis[v] = dis[u] + C[v];
                    q.push(make_pair(dis[v],v));
                }
                pre[Find(v)] = Find(v+1);//不过有没有经过更新,这个点不能再更新了只能更新一次,所以这个点的父亲将指向下一个点
            }
        }
    }
    printf("0");
    for(int i = 2; i <= n; i++){
        if(dis[i] >= INF)
            printf(" -1");
        else
            printf(" %lld",dis[i]-C[i]);//因为在计算时到达每一个点时都加上了这个点的花费,而题目要求只有从这个点到达其他点时才会用这个花费,所以每个点都应减去它本身的花费
    }
    puts("");
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%lld",&L[i]);
        }
        for(int i = 1; i <= n; i++){
            scanf("%lld",&R[i]);
        }
        for(int i = 1; i <= n; i++){
            scanf("%lld",&C[i]);
        }
        init();
        solve();
    }
    return 0;
}


 
这个题解是看的别的博客,道理说的通,但是有点不明白的是如果初始dis【1】 = 0,然后每次算最小花费是队列中取出最小的加起点花费(和题意相符)就不对
望大神指点

错误代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<ll,int> Pir;
const ll INF = 1LL << 60;
const int maxn = 2*1e5+10;
ll L[maxn],R[maxn],C[maxn],dis[maxn];
int n,pre[maxn];
void init(){
    for(int i = 0; i <= n+5; i++){
        pre[i] = i;
        dis[i] = INF;
    }
}
int Find(int x){
    if(x == pre[x])
        return x;
    else return pre[x] = Find(pre[x]);
}
void solve(){
    dis[1] = 0;
    priority_queue<Pir,vector<Pir>,greater<Pir> > q;
    q.push(make_pair(dis[1],1));
    while(!q.empty()){
        Pir st = q.top();//相当于选出最小的作为起点
        q.pop();
        int u = st.second;
        for(int i = -1; i <= 1; i += 2){
            int l = u + i * L[u];
            int r = u + i * R[u];
            if(l > r) swap(l,r);
            l = max(1,l);
            l = min(n+1,l);
            if(l > r) continue;//确定合理距离范围
            for(int v = l;;v++){
                v = Find(v);//直接指向未更新的点
                if(v < 0 || v > n || v > r) break;
                if(dis[v] > dis[u] + C[u]){//为什么是dis[v] > dis[u] + c[u]呢因为题目说从那个点出发需要使用转换器使用转换器要花费cost所以花费的是起点的cost
                    dis[v] = dis[u] + C[u];
                    q.push(make_pair(dis[v],v));
                }
                pre[Find(v)] = Find(v+1);//不过有没有经过更新,这个点不能再更新了只能更新一次,所以这个点的父亲将指向下一个点
            }
        }
    }
    printf("0");
    for(int i = 2; i <= n; i++){
        if(dis[i] >= INF)
            printf(" -1");
        else
            printf(" %lld",dis[i]);
    }
    puts("");
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%lld",&L[i]);
        }
        for(int i = 1; i <= n; i++){
            scanf("%lld",&R[i]);
        }
        for(int i = 1; i <= n; i++){
            scanf("%lld",&C[i]);
        }
        init();
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值