题目链接

环形粒子加速器的能量校准

题目描述

在一个先进的环形粒子加速器中,科学家们需要精确控制其中粒子的能量分布。加速器环道上分布着 个等距的能量监测点,能量值序列为

为了维持磁场稳定,该序列 必须满足“循环单调非递减”性质:序列中至多存在一个“断点”下标 ,使得 ,而在所有其他位置 ,均满足

现在,需要向加速器中注入一个能量值为 的新粒子。任务是,找到一个合适的插入位置,将 插入序列 中,形成一个长度为 的新序列 ,并确保 仍然满足“循环单调非递减”性质。

如果存在多个合法插入位置,请选择使得新粒子在 中下标最小的那个位置。

解题思路

题目的核心要求是在一个满足“循环单调非递减”性质的数组中插入一个新元素,并保持该性质。同时,如果存在多个合法的插入位置,需要选择新元素下标最小的那个。

“循环单调非递减”的序列本质上是一个被旋转过的非递减序列。例如,序列 [4, 5, 1, 2, 3] 就是 [1, 2, 3, 4, 5] 旋转得到的。

考虑到数据规模 ,我们可以采用一个直接且清晰的模拟思路:

  1. 遍历所有可能的插入位置:从下标 (包含 ) 遍历,尝试将新粒子 插入到每一个可能的位置。

  2. 构建临时序列:对于每个插入位置 ,我们构建一个新的、长度为 的临时序列。

  3. 验证性质:编写一个辅助函数,检查这个临时序列是否满足“循环单调非递减”性质。

    • 验证方法是:遍历新序列,统计其中满足 (对于环形序列,最后一个元素与第一个元素也需要比较) 的“断点”数量。
    • 如果断点数量小于或等于 1,那么该序列就满足性质。
  4. 寻找最优解:由于我们是从小到大遍历插入位置的,第一个使得临时序列满足性质的位置,就是新粒子 下标最小的合法位置。此时,直接输出这个临时序列并结束程序即可。

这种方法虽然时间复杂度是 ,但对于本题的数据范围来说完全足够,并且逻辑简单,不易出错。

代码

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

// 检查序列是否满足循环单调非递减性质
bool is_valid(const vector<long long>& arr) {
    int n = arr.size();
    if (n <= 1) {
        return true;
    }
    int break_points = 0;
    for (int i = 0; i < n - 1; ++i) {
        if (arr[i] > arr[i + 1]) {
            break_points++;
        }
    }
    if (arr[n - 1] > arr[0]) {
        break_points++;
    }
    return break_points <= 1;
}

int main() {
    int n;
    cin >> n;
    vector<long long> e(n);
    for (int i = 0; i < n; ++i) {
        cin >> e[i];
    }
    long long x;
    cin >> x;

    for (int i = 0; i <= n; ++i) {
        vector<long long> temp_e = e;
        temp_e.insert(temp_e.begin() + i, x);
        if (is_valid(temp_e)) {
            for (int j = 0; j < temp_e.size(); ++j) {
                cout << temp_e[j] << (j == temp_e.size() - 1 ? "" : " ");
            }
            cout << endl;
            return 0;
        }
    }

    return 0;
}
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    // 检查序列是否满足循环单调非递减性质
    private static boolean isValid(List<Long> arr) {
        int n = arr.size();
        if (n <= 1) {
            return true;
        }
        int breakPoints = 0;
        for (int i = 0; i < n - 1; i++) {
            if (arr.get(i) > arr.get(i + 1)) {
                breakPoints++;
            }
        }
        if (arr.get(n - 1) > arr.get(0)) {
            breakPoints++;
        }
        return breakPoints <= 1;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        List<Long> e = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            e.add(sc.nextLong());
        }
        long x = sc.nextLong();

        for (int i = 0; i <= n; i++) {
            List<Long> tempE = new ArrayList<>(e);
            tempE.add(i, x);
            if (isValid(tempE)) {
                for (int j = 0; j < tempE.size(); j++) {
                    System.out.print(tempE.get(j) + (j == tempE.size() - 1 ? "" : " "));
                }
                System.out.println();
                return;
            }
        }
    }
}
# 检查序列是否满足循环单调非递减性质
def is_valid(arr):
    n = len(arr)
    if n <= 1:
        return True
    break_points = 0
    for i in range(n - 1):
        if arr[i] > arr[i+1]:
            break_points += 1
    if arr[n-1] > arr[0]:
        break_points += 1
    return break_points <= 1

def main():
    n = int(input())
    e = list(map(int, input().split()))
    x = int(input())

    for i in range(n + 1):
        temp_e = e[:i] + [x] + e[i:]
        if is_valid(temp_e):
            print(*temp_e)
            return

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:模拟。通过遍历所有可能的插入点,并逐一验证生成的新序列是否满足题目要求,来找到最小下标的合法插入位置。
  • 时间复杂度。外层循环遍历 个可能的插入位置。对于每个位置,插入操作需要 ,验证性质也需要 。因此总时间复杂度为
  • 空间复杂度。每次循环都需要创建一个长度为 的临时数组来存储插入新粒子后的序列。