CodeXTreme工作室 2024-07-25 11:10 采纳率: 20%
浏览 19
已结题

P7910 [CSP-J 2021] 插入排序

[CSP-J 2021] 插入排序

题目描述

假设比较两个元素的时间为 $\mathcal O(1)$,则插入排序可以以 $\mathcal O(n^2)$ 的时间复杂度完成长度为 $n$ 的数组的排序。不妨假设这 $n$ 个数字分别存储在 $a_1, a_2, \ldots, a_n$ 之中,则如下伪代码给出了插入排序算法的一种最简单的实现方式:

这下面是 C/C++ 的示范代码:

for (int i = 1; i <= n; i++)
    for (int j = i; j >= 2; j--)
        if (a[j] < a[j-1]) {
            int t = a[j-1];
            a[j-1] = a[j];
            a[j] = t;
        }

这下面是 Pascal 的示范代码:

for i:=1 to n do
    for j:=i downto 2 do
        if a[j]<a[j-1] then
            begin
                t:=a[i];
                a[i]:=a[j];
                a[j]:=t;
            end;

给了一个长度为 $n$ 的数组 $a$,数组下标从 $1$ 开始,并且数组中的所有元素均为非负整数。你需要支持在数组 $a$ 上的 $Q$ 次操作,操作共两种,参数分别如下:

$1xv$:这是第一种操作,会将 $a$ 的第 $x$ 个元素,也就是 $a_x$ 的值,修改为 $v$。保证 $1 \le x \le n$,$1 \le v \le 10^9$。注意这种操作会改变数组的元素,修改得到的数组会被保留,也会影响后续的操作

$2~x$:这是第二种操作,假设计算机按照上面的伪代码对 $a$ 数组进行排序,你需要告诉计算机原来 $a$ 的第 $x$ 个元素,也就是 $a_x$,在排序后的新数组所处的位置。保证 $1 \le x \le n$。注意这种操作不会改变数组的元素,排序后的数组不会被保留,也不会影响后续的操作

保证类型 $1$ 的操作次数不超过 $5000$。

输入格式

第一行,包含两个正整数 $n, Q$,表示数组长度和操作次数。

第二行,包含 $n$ 个空格分隔的非负整数,其中第 $i$ 个非负整数表示 $a_i$。

接下来 $Q$ 行,每行 $2 \sim 3$ 个正整数,表示一次操作,操作格式见【题目描述】。

输出格式

对于每一次类型为 $2$ 的询问,输出一行一个正整数表示答案。

样例 #1

样例输入 #1

3 4
3 2 1
2 3
1 3 2
2 2
2 3

样例输出 #1

1
1
2

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int n, Q;
vector<int> a;

int main()
{
    cin >> n >> Q;
    a.resize(n+1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    while (Q--)
    {
        int type;
        cin >> type;
        if (type == 1)
        {
            int x, v;
            cin >> x >> v;
            a[x] = v;
        }
        else if (type == 2)
        {
            int x;
            cin >> x;
            vector<int> b = a;
            sort(b.begin()+1, b.end());
            auto it = find(b.begin()+1, b.end(), a[x]);
            int idx = distance(b.begin(), it);
            cout << idx << endl;
        }
    }
    return 0;
}

按照样例1,我的输出是:

1
1
1
  • 写回答

3条回答 默认 最新

  • 专家-赤兔[在线] 优质创作者: 编程框架技术领域 2024-07-25 11:10
    关注

    引自免费微信小程序:皆我百晓生

    您的代码大致正确,但在处理类型为 2 的操作时,您创建了一个新的数组 b 并对其进行了排序。然而,题目要求在不改变原始数组的情况下确定元素在排序后的位置。因此,您的做法是不符合题目要求的。

    考虑到这一点,我们可以采用插入排序的思路来解决这个问题。我们不需要对整个数组进行排序,只需关注第 x 个元素在其左侧元素中的位置。由于我们假设比较两个元素的时间复杂度为 O(1),我们可以使用插入排序中的关键思想来确定元素在排序后的位置。当遇到一个元素大于或等于 a[x] 时,我们就找到了 a[x] 在排序后的位置。因为插入排序的特性是每次都将当前元素插入到它应该存在的位置,所以我们可以利用这个特性来避免对整个数组进行排序。

    以下是修改后的代码:

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int n, Q;
    vector<int> a;
    
    int findInsertionPosition(int index) {
        int value = a[index];
        int pos = index; // 假设该位置是排序后的位置
        while (pos > 1 && a[pos - 1] > value) { // 寻找插入位置
            a[pos] = a[pos - 1]; // 将大于当前值的元素向后移动一位
            pos--; // 继续向前寻找插入位置
        }
        a[pos] = value; // 将当前值插入到正确的位置
        return pos; // 返回排序后的位置
    }
    
    int main() {
        cin >> n >> Q;
        a.resize(n); // 注意这里只需要将数组大小设置为 n 即可,不需要 n+1
        for (int i = 0; i < n; i++) // 注意这里从 0 开始读取数组元素,因为数组下标从 1 开始而我们的数组是从 0 开始索引的
            cin >> a[i];
    
        while (Q--) {
            int type;
            cin >> type;
            if (type == 1) {
                int x, v;
                cin >> x >> v; // 注意这里从 0 开始读取 x 的值,因为数组下标从 1 开始而我们的索引是从 0 开始的,所以要减一
                a[x - 1] = v; // 更新数组元素的值(注意这里是 x-1)
            } else if (type == 2) {
                int x;
                cin >> x; // 同样注意这里从 0 开始读取 x 的值(x-1)来查找在数组中的索引位置
                cout << findInsertionPosition(x - 1) + 1 << endl; // 返回排序后的位置时加 1 来匹配题目要求的从 1 开始的位置索引(输出时再加一)
            }
        }
        return 0;
    }
    

    对于给定的样例输入,这段代码应该能够正确地输出:123

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 7月27日
  • 创建了问题 7月25日