zoukankan      html  css  js  c++  java
  • 洛谷P5340 [TJOI2019]大中锋的游乐场

    原题链接 [TJOI2019]大中锋的游乐场

    题目描述

    大中锋正在一个游乐场里玩耍。游乐场里有很多娱乐设施,娱乐设施之间相互有道路相连,经过每一条路都需要花费一定的时间。

    为了方便游客,每一个娱乐设施旁都会配有一个小卖部,一部分小卖部会销售可乐,另一部分会销售汉堡。

    由于大中锋十分贪吃,所以每当他走到一个娱乐设施,他都会先去购买一杯可乐或一个汉堡,并把它们吃掉。

    但如果大中锋吃掉的汉堡数量比他喝掉的可乐数量多于k,那他就会感到很渴;如果喝掉的可乐数量比吃掉的汉堡数量多于k,那他就会感到很饿.

    现在大中锋正在第 a 个娱乐设施,他想前往第 b 个娱乐设施,但在他前进的路途中他不希望自己很渴或很饿。

    大中锋想知道自己在路上少花费多少时间。但由于大中锋很懒惰,他不想思考这个问题。你能帮助他解决这个问题吗?

    注意:大中锋非常贪吃,所以他到达每个点的第一件事是去吃(或者喝),才考虑其他的事情,所以在起始点和终点他都会去买汉堡(可乐),

    你也需要保证在这两个点他不会感到很饿或者很渴。

    输入输出格式

    输入格式:

    多样例输入,第一行输入一个正整数 TT 表示样例数。

    对于每一个样例:

    第一行三个数字 n,m,k , n 代表游乐场一共有多少个娱乐设施, m 代表游乐场一共有多少条道路, k 的意义如题面中所述。

    接下来有一行 n 个数字,第 i 个数字代表第 i 个小卖部销售的是什么, 1 代表可乐, 2 代表汉堡。

    接下来有 m 行输入,每行三个数字 p,q,t ,代表从第 p 个娱乐设施到第 q 个娱乐设施有一条双向道路,通过这条道路需要花费 t 单位时间。

    最后一行有两个整数 a,b,代表大中锋想从娱乐设施 a 前往娱乐设施 b 。

    输出格式:

    每组样例输出一行整数 t ,代表大中锋在路上既不会感到很渴也不会感到很饿的情况下,

    从娱乐设施 a 到娱乐设施 b花费的最少时间,如果无法达到,输出 1 。

    输入输出样例

    输入样例#1: 复制
    1
    2 1 1
    1 1
    1 2 1
    1 2
    输出样例#1: 复制
    -1
    输入样例#2: 复制
    1
    2 1 2
    1 1
    1 2 1
    1 2
    输出样例#2: 复制
    1

    说明

    数据范围

    对于 30% 的数据, 1000n50,m1000

    对于 100% 的数据, 10000n10000,m100000,k10,t10000

    对于所有数据,保证 T ≤ 10T10 ,且每个样例点的大数据不超过 22 个。

    题目补充说明

    • 路径不一定是简单路径
    • 大中锋可以多次经过一个节点,同时每次都会取得汉堡/可乐

    题解

    算法:Dijkstra单源最短路径

    题目要求路程上花费的最短时间,故很容易想到最短路算法。

    本题的特色在于求最短路时有一个限定条件——吃汉堡和可乐数量的差的绝对值不能超过k,

    而k的范围非常小(k10),所以可以按照每个状态吃汉堡和可乐数量的差将每1个点拆成2k个点,

    dis(i,j)表示从起点走到i号节点可乐数量-汉堡数量为j花费的最短时间;cola(i)表示i号点的可乐数量(卖可乐则为1,卖汉堡则为-1)

    则dis(i,j)=min{dis(k,j-cola(i))+路径长度},用dijkstra即可

    实现

    代码很简单,核心是dijkstra部分:

    1.初始化

    (1)定义node表示状态,用优先队列priority_queue存储

    struct node{
        int idx,tim,cola;
        node(int x,int y,int z){
            idx=x; tim=y; cola=z;
        }
        friend inline bool operator<(node x,node y){
            if(x.tim!=y.tim)
                return x.tim>y.tim;
            return x.cola>y.cola;
        }
    };

    (2)宽搜前插入起点,可乐数量为k(为了防止可乐数量为负数,加上基数k,控制可乐数量在0~2k之间)

    que.push(node(S,0,K+cola[S]));

    2.转移:

    根据dis(i,j)=min{dis(k,j-cola(i))+路径长度},套用dijkstra转移方程

    for(int i=head[idx];i;i=nxt[i]){
        int ii=col+cola[to[i]];
        if((ii>=0&&ii<=K*2)&&(!vis[to[i]][ii]&&tim+cst[i]<dis[to[i]][ii])){
            dis[to[i]][ii]=tim+cst[i];
            que.push(node(to[i],dis[to[i]][ii],ii));
        }
    }

    代码 

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 const int INF=0x3f3f3f3f,MAXN=1e4+50,MAXM=2e5+50,MAXK=21;
     5 int sz,head[MAXN],nxt[MAXM],to[MAXM],cst[MAXM];
     6 inline void add(int x,int y,int z){
     7     nxt[++sz]=head[x]; head[x]=sz; to[sz]=y; cst[sz]=z;
     8     nxt[++sz]=head[y]; head[y]=sz; to[sz]=x; cst[sz]=z;
     9 }
    10 int N,M,K,S,T,cola[MAXN];
    11 inline void Init(){
    12     scanf("%d%d%d",&N,&M,&K);
    13     for(int i=1;i<=N;i++){
    14         scanf("%d",cola+i);
    15         if(cola[i]==2)
    16             cola[i]=-1;
    17     }
    18     for(int i=1;i<=M;i++){
    19         int ii,jj,kk;
    20         scanf("%d%d%d",&ii,&jj,&kk);
    21         add(ii,jj,kk);
    22     }
    23     scanf("%d%d",&S,&T);
    24 }
    25 struct node{
    26     int idx,tim,cola;
    27     node(int x,int y,int z){
    28         idx=x; tim=y; cola=z;
    29     }
    30     friend inline bool operator<(node x,node y){
    31         if(x.tim!=y.tim)
    32             return x.tim>y.tim;
    33         return x.cola>y.cola;
    34     }
    35 };
    36 int dis[MAXN][MAXK],vis[MAXN][MAXK];
    37 inline void Dijkstra(){
    38     memset(dis,0x3f,sizeof(dis));
    39     memset(vis,0,sizeof(vis));
    40     priority_queue<node> que;
    41     que.push(node(S,0,K+cola[S]));
    42     while(que.size()){
    43         node cur=que.top();
    44         que.pop();
    45         int idx=cur.idx,tim=cur.tim,col=cur.cola;
    46         if(vis[idx][col]||tim>dis[idx][col])
    47             continue;
    48         vis[idx][col]=1;
    49         for(int i=head[idx];i;i=nxt[i]){
    50             int ii=col+cola[to[i]];
    51             if((ii>=0&&ii<=K*2)&&(!vis[to[i]][ii]&&tim+cst[i]<dis[to[i]][ii])){
    52                 dis[to[i]][ii]=tim+cst[i];
    53                 que.push(node(to[i],dis[to[i]][ii],ii));
    54             }
    55         }
    56     }
    57 }
    58 int ans=INF;
    59 inline void Output(){
    60     for(int i=0;i<=2*K;i++)
    61         if(dis[T][i]<ans)
    62             ans=dis[T][i];
    63     printf("%d
    ",ans==INF?-1:ans);
    64 }
    65 int Task;
    66 int main(){
    67     scanf("%d",&Task);
    68     while(Task--){
    69         Init();
    70         Dijkstra();
    71         Output();
    72     }
    73     return 0;
    74 }
    View Code
  • 相关阅读:
    Palindrome Partitioning
    triangle
    Populating Next Right Pointers in Each Node(I and II)
    分苹果(网易)
    Flatten Binary Tree to Linked List
    Construct Binary Tree from Inorder and Postorder Traversal(根据中序遍历和后序遍历构建二叉树)
    iOS系统navigationBar背景色,文字颜色处理
    登录,注销
    ios 文字上下滚动效果Demo
    经常崩溃就是数组字典引起的
  • 原文地址:https://www.cnblogs.com/guoshaoyang/p/10846224.html
Copyright © 2011-2022 走看看