bluecode-2024D-学生重新排队

124 阅读4分钟

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 128MB,其他语言 256MB
难度:困难
分数:100 OI排行榜得分:16(0.1分数+2难度)

描述

n个学生排成一排,学生编号分别是1到n,n为3的整倍数。 

老师随机抽签决定将所有学生分成个3人的小组,n=3*m

为了便于同组学生交流,老师决定将小组成员安排到一起,也就是同组成员彼此相连,同组任意两个成员之间无其它组的成员。 

因此老师决定调整队伍,老师每次可以调整任何一名学生到队伍的任意位置,计为调整了一次,请计算最少调整多少次可以达到目标。 

注意:对于小组之间没有顺序要求,同组学生之间没有顺序要求

输入描述

两行字符串,空格分隔表示不同的学生编号。 

第一行是学生目前排队情况第二行是随机抽签分组情况,从左开始每3个元素为一组n为学生的数量,n的范围为[3,900],n一定为3的整数倍。第一行和第二行的元素个数一定相同。

输出描述

老师调整学生达到同组彼此相连的最小次数

用例输入 1 **

7 9 8 5 6 4 2 1 3
7 8 9 4 2 1 3 5 6

用例输出 1 **

1

提示

学生目前排队情况:7 9 8 5 6 4 2 1 3 

学生分组情况:[7 8 9]、[4 2 1]、[3 5 6] 

将3调整到4之前,队列调整为7 9 8 5 6 3 4 2 1 

那么三个小组成员均彼此相连[7 9 8]、[5 6 3]、[4 2 1] 

输出:1

#include <iostream>
#include <vector>
#include <unordered_map>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <sstream>

using namespace std;

// 用于检查第idx组中的三个元素是否完全相同
bool checkGroup(const vector<int>& nums, int idx) {
    return nums[idx * 3] == nums[idx * 3 + 1] && nums[idx * 3 + 2] == nums[idx * 3 + 1];
}

// 用于检查nums是否已经排好队的函数
// 如果每3个元素都相等,则说明这一组已经排好
bool checkAll(const vector<int>& nums, int startIdx, int endIdx) {
    for (int idx = startIdx; idx <= endIdx; ++idx) {
        if (!checkGroup(nums, idx)) {
            return false;
        }
    }
    return true;
}

// 更新尚未排好序的组的区间startGroup和endGroup
pair<int, int> updateGroupInterval(const vector<int>& nums, int startGroup, int endGroup) {
    int i = startGroup, j = endGroup;
    while (checkGroup(nums, i)) {
        i++;
    }
    while (checkGroup(nums, j)) {
        j--;
    }
    return {i, j};
}

// 用于进行蒙特卡洛模拟的函数
int mcSimulation(vector<int>& nums, int n) {
    // 蒙特卡洛模拟的次数,该数值可以自行进行调整
    int T = 10000;
    // 初始化最多的调整次数,该数值可以自行进行调整
    int ans = 10;

    // T次蒙特卡洛模拟
    for (int t = 0; t < T; ++t) {
        // 每一次蒙特卡洛模拟,都需要重新获得原nums的副本
        // 对副本numsNew进行排队的模拟
        vector<int> numsNew = nums;
        // 移动的次数最多不超过ans次数
        int time = 0;
        // 尚未排好序的组的区间
        int startIdx = 0, endIdx = n - 1;
        while (time < ans) {
            // 本次模拟的移动次数+1
            time++;

            // 更新尚未排好序的组的区间
            auto interval = updateGroupInterval(nums, startIdx, endIdx);
            startIdx = interval.first;
            endIdx = interval.second;

            // 随机选择组编号group_i,是取出的索引i所在的组
            int groupI = rand() % (endIdx - startIdx + 1) + startIdx;

            // 计算得到取出的位置i
            int i = groupI * 3 + rand() % 3;

            // 随机选择组编号group_j,是取出的索引j所在的组
            int groupJ;
            do {
                groupJ = rand() % (endIdx - startIdx + 1) + startIdx;
            } while (groupJ == groupI);

            // 计算得到插入的位置j
            int j = groupJ * 3 + rand() % 3;

            // 取出索引i位置的元素num
            int num = numsNew[i];
            numsNew.erase(numsNew.begin() + i);
            numsNew.insert(numsNew.begin() + j, num);

            // 判断numsNew是否已经排好队
            // 如果是则直接更新ans
            if (checkAll(numsNew, startIdx, endIdx)) {
                ans = time;
                break;
            }
        }
    }
    return ans;
}

int main() {
    srand(time(0));
    // 输入两个数组
    vector<int> nums1, nums2;
    string input;
    
    getline(cin, input);
    istringstream iss1(input);
    int num;
    while (iss1 >> num) {
        nums1.push_back(num);
    }

    getline(cin, input);
    istringstream iss2(input);
    while (iss2 >> num) {
        nums2.push_back(num);
    }

    // m个同学
    int m = nums1.size();
    // n个组
    int n = m / 3;

    // 构建映射哈希表
    unordered_map<int, int> map;
    // 对于nums2,每三个元素映射到组编号i
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < 3; ++j) {
            map[nums2[i * 3 + j]] = i;
        }
    }

    // 根据映射哈希表,构建数组nums
    // nums数组包含0至(n//3-1)一共n//3个元素
    // 每一个元素出现3次
    // 形如:[0, 0, 0, 1, 1, 2, 2, 2, 1]
    vector<int> nums(m);
    for (int i = 0; i < m; ++i) {
        nums[i] = map[nums1[i]];
    }

    // 判断数组nums是否已经是已排好队的
    // 如果是则直接输出0,否则进行蒙特卡洛模拟过程
    if (checkAll(nums, 0, n - 1)) {
        cout << 0 << endl;
    } else {
        cout << mcSimulation(nums, n) << endl;
    }

    return 0;
}