Jumping on Tiles||Codeforces Round #820 (Div. 3) C题题解

C. Jumping on Tiles

题面翻译(来自洛谷)

题目大意

给定一个字符串 s s s,polycarp 欲从字符串首跳到字符串末 ( s 1 s_1 s1 s n s_n sn,其中 n n n 表示该字符串长度)。

假设 polycarp 现从 a i a_i ai 跳到了 a j a_j aj 我们定义这一次跳跃的权值为 ∣ index ⁡ ( a i ) − index ⁡ ( a j ) ∣ |\operatorname{index}(a_i) - \operatorname{index}(a_j)| index(ai)index(aj),其中 index ⁡ \operatorname{index} index
表示该字符在字母表中的序号 ( 如 index ⁡ ( ′ a ′ ) = 1 ,    index ⁡ ( ′ z ′ ) = 26 \operatorname{index}('a') = 1, \; \operatorname{index}('z') = 26 index(a)=1,index(z)=26 )。

请构造出一种在保证权值和最小的情况下经过的字符最多的跳跃方案 ( 当然,同一个字符只能经过一次,其中同一个仅指在字符串中的位置相同 )。

输入格式

第一行包含一个整数 t    ( 1 ⩽ t ⩽ 1 0 4 ) t \; (1 \leqslant t \leqslant 10^4) t(1t104) ,表示测试样例组数。

对于每组测试样例,包含一行一个字符串 s    ( 2 ⩽ ∣ s ∣ ⩽ 2 ⋅ 1 0 5 ) s \; (2 \leqslant |s| \leqslant 2 \cdot 10^5) s(2s2105),意义见题面。

输出格式

对于每组测试样例,第一行包含两个用空格隔开的整数 c o s t cost cost m m m 分别表示 最小权值和 和 最大经过的字符数。

第二行包含 m m m 个整数,分别表示沿途经过的所有字符位置。( 例如输出 1    4    3    5 1 \; 4 \; 3 \; 5 1435 表示跳跃路径为 s 1 s_1 s1 s 4 s_4 s4 s 3 s_3 s3 s 5 s_5 s5 ) 数与数之间用空格隔开。

T r a n s l a t e d    b y    Z i g h Translated \; by \; Zigh TranslatedbyZigh

题目描述

Polycarp was given a row of tiles. Each tile contains one lowercase letter of the Latin alphabet. The entire sequence of tiles forms the string $ s $ .

In other words, you are given a string $ s $ consisting of lowercase Latin letters.

Initially, Polycarp is on the first tile of the row and wants to get to the last tile by jumping on the tiles. Jumping from $ i $ -th tile to $ j $ -th tile has a cost equal to $ |index(s_i) - index(s_j)| $ , where $ index© $ is the index of the letter $ c $ in the alphabet (for example, $ index( $ ‘a’ $ )=1 $ , $ index( $ ‘b’ $ )=2 $ , …, $ index( $ ‘z’ $ )=26 $ ) .

Polycarp wants to get to the $ n $ -th tile for the minimum total cost, but at the same time make maximum number of jumps.

In other words, among all possible ways to get to the last tile for the minimum total cost, he will choose the one with the maximum number of jumps.

Polycarp can visit each tile at most once.

Polycarp asks you to help — print the sequence of indices of string $ s $ on which he should jump.

输入格式

The first line of the input contains an integer $ t $ ( $ 1 \le t \le 10^4 $ ) — the number of test cases in the test.

Each test case is given by the string $ s $ ( $ 2 \le |s| \le 2 \cdot 10^5 $ ), where $ |s| $ — is the length of string $ s $ . The string $ s $ consists of lowercase Latin letters.

It is guaranteed that the sum of string lengths $ s $ over all test cases does not exceed $ 2 \cdot 10^5 $ .

输出格式

The answer to each test case consists of two lines.

In the first line print two integers $ cost $ , $ m $ , where $ cost $ is the minimum total cost of the path, and $ m $ is the maximum number of visited tiles Polycarp can make to get to $ n $ -th tiles for the minimum total cost $ cost $ (i.e. the number of jumps is $ m-1 $ ).

In the next line print $ m $ different numbers $ j_1, j_2, \dots, j_m $ ( $ 1 \le j_i \le |s| $ ) — the sequence of indices of the tiles Polycarp will jump on. The first number in the sequence must be $ 1 $ (that is, $ j_1=1 $ ) and the last number must be the value of $ |s| $ (that is, $ j_m=|s| $ ).

If there are multiple answers, print any of them.

样例 #1

样例输入 #1

6
logic
codeforces
bca
aaaaaaaaaaa
adbaadabad
to

样例输出 #1

9 4
1 4 3 5
16 10
1 8 3 4 9 5 2 6 7 10
1 2
1 3
0 11
1 8 10 4 3 5 7 2 9 6 11
3 10
1 9 5 4 7 3 8 6 2 10
5 2
1 2

提示

In the first test case, the required path corresponds to the picture:


In this case, the minimum possible total cost of the path is achieved. Since $ index( $ ‘l’ $ )=12 $ , $ index( $ ‘o’ $ )=15 $ , $ index( $ ‘g’ $ )=7 $ , $ index( $ ‘i’ $ )=9 $ , $ index( $ ‘c’ $ )=3 $ , then the total cost of the path is $ |12-9|+|9-7|+|7-3|=3+2+4=9 $ .


题解

比赛的时候不知道什么原因在前面一段时间做出这道题的人很少,甚至专门发了一个announcement来叫参赛者去读提示和看图。我看到这个题时候也是马上有了思路,但交了六次都是WA的,怎么也找不出问题(甚至到了写这篇题解前)。

思路(贪心)

如果不考虑其他元素,从起点到终点的代价就是它们两个字符的ASCII码的差点绝对值,所以我们贪心地考虑添加其他元素,使得这个答案串与原字符串中找出与起始字符到结束字符的单调性完全一致,这样我们在最低的花费开销顺便得到了额外的跳跃步数,大可不必考虑元素跳跃的先后顺序,请多看图可以理解。

答案
首先最小的花费就是字符串头和尾元素的ASCII码的差点绝对值 ∣ s ⁡ [ 0 ] − s ⁡ [ s . l e n g t h ( ) − 1 ] ∣ |\operatorname{s}[0] - \operatorname{s}[s.length()-1]| s[0]s[s.length()1]如果我们对字符串进行排序,找到原头元素和原尾元素的位置,将它们中间的值添加进答案并不会对花费产生影响,同时他们之间的距离就是跳跃次数的最大值。

证明
如果到达了排序字符串后原起点和终点以外的值,最终为了要到达终点,无论是往头还是往尾,都会往回走,产生额外的开销。

实现
很快我想出一个排序的做法,因为我们要输出跳跃的砖块的编号,所以我们要保存他们的值,我就用了一个pair,两个元素分别存储原字符串元素和在原数组的下标(和离散化很像,是吧?)。然后在排序后的字符串中找到头和尾的位置,在他们的范围内顺序遍历并将原下标存入答案。

但我当时的解法是WA了并且我找不出原因,后面结束后看测试数据发现如果头元素在字符串中重复出现后就不能保证原头位于第一个(尾的情况类似)

改进&AC代码
只用对除了头和尾的部分进行排序,然后在遍历这段排序后的部分,加上满足大于等于头和小于等于尾的条件判断(此前已经交换顺序使得头始终是小的),满足就加入答案。

与我前面的做法相比,在排序的范围发生了变化并且不用查找原头和原尾在排序后的位置。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
#define PY puts("Yes")
#define PN puts("No")
pair<char, int> pi[N];
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        string s;
        cin >> s;
        int n = s.length();
        char a = s[0], b = s[s.length() - 1];
        for (int i = 0; i < n; i++)
        {
            pi[i].first = s[i];
            pi[i].second = i + 1;
        }
        sort(pi + 1, pi + n - 1);
        vector<int> ans;
        bool reversed = false;
        if (a > b) //始终让a作为起点,b作为终点,即满足 a < b
        {
            swap(a, b);
            reversed = true;
        }
        for (int i = 1; i < n - 1; i++)
        {
            if (pi[i].first >= a && pi[i].first <= b) //注意取等于号
                ans.emplace_back(pi[i].second);
        }
        int cost = b - a;
        int step = 2 + ans.size();
        cout << cost << " " << step << endl;
        cout << 1 << " "; //题目规定必须以1为开头,字符串的长度作为结尾
        if (reversed)
            reverse(ans.begin(), ans.end());
        for (int i = 0; i < ans.size(); i++)
            cout << ans[i] << " ";
        cout << n;
        cout << endl;
    }
    return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值