Codeforces Gym 101173 J. Jazz Journey

本文介绍了一个关于序列路径最小花费的问题,并提供了一种解决方案。通过将路径分堆处理并结合不同类型的机票信息,计算出完成给定序列所需的最低费用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

J - Jazz Journey

题意

序列 a1,a2,...,ad 表示需要从 aiai+1 。现在有 m 种不同的飞机票 ak dk tk pk,每种表示从 akdk,若 tk 为 O 表示单程,为 R 表示往返(但仅有先用该票搭乘 akdk 后才可用该票 dkak

要求对给定的序列求所需的最小花费。保证必定有解。

解题思路

首先根据 aiai+1 将行程进行分堆,单独保存进行处理。处理结果应该如下:

序列为: 1 2 3 1 2 1 3 2 4 1
分堆结果:

      (1 -> 2)  正向 正向 反向
      (2 -> 3)  正向 反向
      (1 -> 3)  反向 正向
      (2 -> 4)  正向
      (1 -> 4)  反向

根据 m 种机票的信息,对每条路线记录四种价格: 正向往返, 反向往返, 正向单程, 反向单程
若不存在则默认记为 INF 。
其中应注意四种价格部分存在转换关系,应记录其可能的最低价格,例如:选择正向往返可放弃反向的返程。

对同一条路线,暴力计算三种不同方式的购票方案即可:

  • 只买单程票

  • 优先考虑正向往返票,反向往返票次之,最后单程票

  • 优先考虑反向往返票,正向往返票次之,最后单程票

统计总价格。

代码

#include<bits/stdc++.h>
using namespace std;
const long long inf = 1ll<< 60;
const int D = 300000 + 10;
int n, d, m, a[D], mpidx = 0;
char tk;
vector<bool> trip[D];
long long price[D][4];
map<pair<int, int>, int> mp;
pair<int, int> p;
int main()
{
    scanf("%d %d", &n, &d);
    for(int i=1;i<=d;i++)
        scanf("%d", &a[i]);
    for(int i=1;i<d;i++)
    {
        if(a[i] > a[i+1])   p = make_pair(a[i+1], a[i]);
        else    p = make_pair(a[i], a[i+1]);
        if(mp.find(p) == mp.end())
            mp[p] = ++mpidx;
        trip[mp[p]].push_back(a[i]<a[i+1]?true:false);
    }

    for(int i=1;i<=mpidx;i++)
        for(int j=0;j<4;j++)
            price[i][j] = inf;
    scanf("%d", &m);
    for(int i=1, sk, dk, pk, pos=0, midx;i<=m;i++,pos=0)
    {
        scanf("%d %d %c %d", &sk, &dk, &tk, &pk);
        if(tk == 'O')   pos+=2;
        if(sk > dk) p = make_pair(dk, sk),  pos++;
        else    p = make_pair(sk, dk);
        if(mp.find(p) == mp.end())  continue;
        midx = mp[p];
        price[midx][pos] = min(price[midx][pos], (long long)pk);
    }
    long long ans = 0;
    for(int i=1;i<=mpidx;i++)
    {
        if(price[i][0] < price[i][2])   price[i][2] = price[i][0];
        if(price[i][1] < price[i][3])   price[i][3] = price[i][1];
        if(price[i][2]+price[i][3] < price[i][0])  price[i][0] = price[i][2]+price[i][3];
        if(price[i][2]+price[i][3] < price[i][1])  price[i][1] = price[i][2]+price[i][3];
        long long tmp[3] = {0, 0, 0};

        // 全部单程
        for(int j=0;j<trip[i].size();j++)
        {
            tmp[0] += price[i][3-trip[i][j]];
            if(tmp[0] >= inf) {
                tmp[0] = inf;   break;
            }
        }

        // 优先 正向往返
        if(price[i][0] == inf)  tmp[1] = inf;
        else {
            deque<bool> que;
            for(int j=0;j<trip[i].size();j++)
            {
                if(trip[i][j] == true)
                    que.push_back(true);
                else {
                    if(!que.empty() && que.back() == true)
                        que.pop_back(), tmp[1] += price[i][0];
                    else    que.push_back(false);
                }
            }
            int cntTrue = 0, cntFalse = 0;
            while(!que.empty()) cntTrue += que.front(), cntFalse += que.front()?false:true,   que.pop_front();
            if(price[i][2]+price[i][3] > price[i][1]) {
                int mx = min(cntTrue, cntFalse);
                tmp[1] += mx * price[i][1];
                cntTrue -= mx,  cntFalse -= mx;
            }
            tmp[1] += price[i][2] * cntTrue + price[i][3] * cntFalse;
        }

        // 优先 反向往返
        if(price[i][1] == inf)  tmp[2] = inf;
        else {
            deque<bool> que;
            for(int j=0;j<trip[i].size();j++)
            {
                if(trip[i][j] == false)
                    que.push_back(false);
                else {
                    if(!que.empty() && que.back() == false)
                        que.pop_back(), tmp[2] += price[i][1];
                    else    que.push_back(true);
                }
            }
            int cntTrue = 0, cntFalse = 0;
            while(!que.empty()) cntTrue += que.front(), cntFalse += que.front()?false:true,   que.pop_front();
            if(price[i][2]+price[i][3] > price[i][0]) {
                int mx = min(cntTrue, cntFalse);
                tmp[2] += mx * price[i][0];
                cntTrue -= mx,  cntFalse -= mx;
            }
            tmp[2] += price[i][2] * cntTrue + price[i][3] * cntFalse;
        }
        sort(tmp, tmp+3);
        ans += tmp[0];
    }
    printf("%I64d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值