[算法题] Codefun2000 6.3日模拟赛

232 阅读3分钟

www.codefun2000.com 是一个收集各种笔试题目,供大家学习交流,体会笔试难度的OJ网站。

题目链接

由于第三题的一些细节一直没有思考通,故拖到现在才发出来。

第一题

题目描述

塔子哥是一个数学爱好者,他经常思考各种数字之间的奥秘。今天,他想要解决一个有趣的问题:如何在一个数字中插入一个数字,以得到最大的结果?他发现这个问题很有挑战性,因为他必须找到一种方法,能够在给定的数字中插入一个数字,使得插入后的结果最大。于是,他开始思考并设计了一种算法来解决这个问题。
算法的核心思想是:首先将第一个数字转化为字符串,然后将第二个数字插入到字符串的不同位置,得到多个可能的结果。最后,从中选择最大的一个数字作为最终的结果。 塔子哥已经写好了这个算法,并请你帮忙测试一下。请编写一个程序,输入塔子哥手上的两个数字,输出插入后得到的最大数字。(前导零正常输出)

输入描述

输入第一行为一个数字TT,表示有TT组测试样例。接下来的TT行对于每一组数据,包含一行有2个空格隔开的整数,表示任意正整数 aa 和待插入的数字 bb
其中, 1T10,1a105,0b91 \leq T \leq 10, 1 \leq a \leq 10^5, 0 \leq b \leq 9

输出描述

对于每组数据,输出一行,包括一个整数,得到最大的整数。

样例

输入

3
9
0 0 1 0 0 1 0 0 1
6
1 1 0 1 1 0
4
1 0 0 0

输出

5 4 4 4 3 5 3 2 6
3 2 3 2 1 4
0 3 2 1

思路

这题是一个很简单的贪心算法,即从最高位开始找,找到第一个比数字bb小的位,并将bb插入到该位之前,如果没有找到比bb大的数位,则将bb插入到最后 这道题我主要犯了两个错误,导致没能成功ac。

  1. 没有考虑溢出的情况(经典错误)。但事实上即使用上了long long依旧有部分用例溢出,
  2. 虽然题中说到1a1051 \leq a \leq 10^5,但事实上有如下测试用例,不知道题目最后提到的“(前导零正常输出)”是否与这个问题相关。
    1
    0 1

另外还有一些小问题:

  1. C++的cin可以直接将标准输入的内容放入string类型中,而不一定是整型类型。没必要先以整型接收,再使用to_string()进行转换。
// 将输入放入字符串中
string a;
cin >> a;
// 将输入放入长整型中
long long a;
cin >> a;
  1. 平时习惯了Leetcode的核心代码模式,因此总是习惯于将答案存储在一个变量中再做输出,而这在ACM模式中其实完全没有必要。以本题为例,我用了一个链表来存储a的每一个数字,并将b实际地插入了a,再将链表转为整型输出。而实际上,并不需要实际地将b插入a中,只需要先输出a的前一部分,再输出b,最后输入a的后一部分即可。

code

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int T;
    cin >> T;
    for(int i = 0; i < T; ++i) {
        string a;
        int b;
        cin >> a >> b;
        int idx = -1; 
        for(int i = 0; i < a.size(); i++){
            int t = a[i] - '0';
            if(b > t){
                idx = i;
                break;
            }
        }
        // 没找到比b小的数字,将b插入a的最后
        if(idx == -1) idx = a.size();
        // 先输出a的前一部分,输出b,最后输入a的后一部分
        cout << a.substr(0, idx) << b << a.substr(idx) << endl;
    }
    return 0;
}

对于每个测试样例,事件复杂度为O(n)O(n)

第二题

简单的前缀和加后缀和,略

第三题

题目描述

塔子哥是一位火车车厢调度员。
这一天,一列带有 n 个编号车厢的列车进站了,编号为 1~n 。但是,这些车厢的编号并不是按从左到右递增的顺序,为了后面的工作方便展开,塔子哥想把火车调整成从左到右递增的顺序,即 [1,2,3,……n] ,但他只能进行一种操作:首先他要在火车中任意选择两个车厢取出。然后他将两个车厢中较大的放在火车的最右边,较小的放在火车的最左边。 他可以进行无限次这样的操作,他想知道最少需要多少次才能将火车调整为从左到右递增的顺序。

输入描述

第一行是数组的长度: nn . 第二行有n个数字 pi(i=1,2,3...,n)p_i(i = 1,2,3...,n)表示从左到右的车厢编号。
其中,1n50000,1pin 1 \leq n \leq 50000, 1 \leq p_i \leq n 

输出描述

输出一个整数,表示最少需要操作多少次。

样例

样例输入

7
1 3 5 2 4 6 7

样例输出

3

思路

我开始时理解错了题意,以为是选中两个元素然后进行位置交换,但实际上是选择两个元素,将其中大的放在火车的最右端,小的放在火车的最左端。

这道题与Codeforce的1792C Min Max Sort一致,我翻译了Tutorial里的题解,补充了部分自己的理解,并修改了原代码的贴于下文。

考虑最简单的情况,如果火车编号本来就是升序的,那么不需要操作,答案为00。如果不是升序的,最后一次操作一定是选择选择火车中最大和最小编号的两个车厢,即11nn,否则操作结束后不可能第一个编号是11,最后一个编号是nn
接下来考虑区间[2,n1][2,n-1],如果区间[2,n1][2,n-1]里的编号是升序的,那么答案就是1,否则,完全类似的,最后第二个操作应该是选择22n1n-1;如果区间[3,n2][3,n-2]的编号是升序的,那么答案就是2,否则,完全类似的,最后第三个操作应该是选择33n2n-2;依此类推,直到剩下的区间是升序的或者为空为止。
注意这里的元素值是每个编号在原排列中的位置,而不是原排列中的每个位置对应的编号值(但这点我始终没有思考明白,为什么使用后者是不正确的,我还得再思考思考,也希望大家能帮我解答一下)。

如此,本题就转换为如何找到最大的kk使得[k,nk+1][k,n-k+1]区间是升序的。
这就很简单了,从最大的kk开始,向两边扩展,直到左侧元素比右侧的大,或者右侧元素比左侧小为止。另外一个方法是使用二分搜索。

code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cin >> n;
    vector<int> pos(n + 1);
    for (int i = 0; i < n; ++i) {
        int x;
        cin >> x;
        pos[x] = i;
    }
    
    int l = (n + 1) / 2, r = (n + 2) / 2;
    while (l > 0 && (l == r || (pos[l] < pos[l + 1] && pos[r - 1] < pos[r]))) {
        --l;
        ++r;
    }
    cout << (n - r + l + 1) / 2 << '\n';
}

第四题

模拟,但当时我时是没看懂题意的,我太菜了。