J - Jazz Journey
题意
序列 a1,a2,...,ad 表示需要从 ai→ai+1 。现在有 m 种不同的飞机票 ak dk tk pk
,每种表示从 ak→dk,若 tk 为 O
表示单程,为 R
表示往返(但仅有先用该票搭乘 ak→dk 后才可用该票 dk→ak 。
要求对给定的序列求所需的最小花费。保证必定有解。
解题思路
首先根据 ai→ai+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);
}