华为OD机试 - 小朋友分组最少调整次数 - 贪心策略(Java 2024 E卷 200分)

在这里插入图片描述

华为OD机试 2024E卷题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

一、题目描述

n (3 ≤ n ≤ 90000 且可以整除 3) 个学生排成一排,学生编号分别是 1 到 n,n 为 3 的整数倍数,老师随机抽签决定将所有学生分成 m 个 3 人的小组(n = 3 * m)。

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

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

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

二、输入描述

第一行输入初始排列顺序序列

第二行输入分组排列顺序序列

三、输出描述

输出一个整数k,表示至少调整k次。

四、测试用例

测试用例1:

1、输入

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

2、输出

1

3、说明

只有一个组(例如 [4, 2, 8] 对应的组号)需要调整位置,使其成员在最终排列中相邻。这意味着只需要一次调整就能实现目标排列。因此,输出结果为 1,表示至少需要一次调整。

测试用例2:

1、输入

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

2、输出

0

3、说明

每组3个学生的编号在初始排列和目标排列中的组号顺序已经是对应的,并且组内成员在初始排列中已经彼此相邻。
通过检查可以发现,每个组的成员在初始排列中已经满足相邻的条件,因此不需要进行任何调整。

五、解题思路

这道题的目的是通过最少的调整次数,将一组学生按照指定的目标顺序排列,使得同组的学生在最终排列中彼此相连。题目的核心在于寻找一种策略,能够有效地将学生移动到合适的位置,同时最小化移动次数。

在计算调整次数时,采用了一种贪心的策略,优先处理需要最少调整的情况(如两两交换),从而减少整体调整次数。

(1)将学生编号映射为组号:

我们的目标是将学生编号转化为组号。因为每个组有3个学生,我们可以通过将目标排列中的每个学生编号映射到其组号(即编号除以3)来得到初始序列中每个学生的目标组号。

(2)记录组号的位置:

我们需要记录每个组号在初始排列中的位置。为此,我们创建一个数组列表,每个列表存储对应组号在初始排列中出现的位置。

(3)计算最少调整次数:

我们通过遍历每个组的位置列表来计算是否需要调整。如果一个组的所有成员在初始序列中已经连续排列,那么不需要调整;否则,需要进行一次调整。

如果一个组只有一个成员,则需要两次调整,因为要将其与另两个成员排在一起。如果有两个成员且它们间隔较大,也需要一次调整。

(4)计算组间交错:

为了避免多余的调整,计算组间的交错情况。如果一个组的成员之间有其他组的成员插入,这表明需要额外的调整。通过遍历整个组号序列,记录每个组号的出现次数,我们可以发现是否存在组间交错,并调整次数。

(5)输出最小调整次数:

最后,我们取前述计算出的两种调整方法的最小值作为最终的最小调整次数。

六、Java算法源码

public class OdTest01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读取初始排列顺序
        int[] initialOrder = Arrays.stream(scanner.nextLine().split(" "))
                .mapToInt(Integer::parseInt)
                .toArray();

        // 读取分组排列顺序
        int[] targetOrder = Arrays.stream(scanner.nextLine().split(" "))
                .mapToInt(Integer::parseInt)
                .toArray();

        int studentCount = initialOrder.length;

        // 创建一个映射数组,用于将学生编号映射到目标序列中的组号
        int[] groupMapping = new int[studentCount + 1];
        for (int i = 0; i < studentCount; i++) {
            groupMapping[targetOrder[i]] = i / 3;
        }

        // 将初始顺序转换为组号数组
        int[] groupOrder = Arrays.stream(initialOrder)
                .map(student -> groupMapping[student])
                .toArray();

        // 创建一个数组列表的数组,用于存储每个组号在初始序列中出现的位置
        ArrayList<Integer>[] groupPositions = new ArrayList[studentCount / 3];
        for (int i = 0; i < studentCount / 3; i++) {
            groupPositions[i] = new ArrayList<>();
        }

        // 遍历组号数组,记录每个组号出现的位置
        for (int i = 0; i < groupOrder.length; i++) {
            groupPositions[groupOrder[i]].add(i);
        }

        int minimumMoves = 0;

        // 遍历每个组的位置列表,计算需要的最少移动次数
        for (ArrayList<Integer> positions : groupPositions) {
            if (positions.size() == 3) {
                // 如果一个组有3个成员,检查他们是否连续
                if (positions.get(1) - positions.get(0) > 1 || positions.get(2) - positions.get(1) > 1) {
                    minimumMoves++;
                }
            } else if (positions.size() == 2) {
                // 如果一个组有2个成员,检查他们是否间隔过大
                if (positions.get(1) - positions.get(0) > 2) {
                    minimumMoves++;
                }
            } else if (positions.size() == 1) {
                // 如果一个组只有1个成员,最坏情况需要两次调整
                minimumMoves += 2;
            }
        }

        // 通过计算组间的交错情况,可能减少调整次数
        HashSet<Integer> encounteredGroups = new HashSet<>();
        int overlapAdjustments = 0;
        for (int group : groupOrder) {
            if (!encounteredGroups.contains(group)) {
                encounteredGroups.add(group);
            } else {
                overlapAdjustments++;
            }
        }

        // 取两种调整方法的最小值
        minimumMoves = Math.min(minimumMoves, overlapAdjustments);

        // 输出最小调整次数
        System.out.println(minimumMoves);
    }
}

七、效果展示

1、输入

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

2、输出

1

3、说明

(1)分组:

初始排列中 [7, 9, 8] 是第一组,[5, 6, 4] 是第二组,[2, 1, 3] 是第三组。

目标排列中 [7, 8, 9] 是第一组,[4, 2, 1] 是第二组,[3, 5, 6] 是第三组。

(2)检查初始排列的连续性:

在初始排列中,第一组 [7, 9, 8] 中的成员不完全连续(9 和 8 被分开)。

第二组 [5, 6, 4] 也不符合目标顺序。

第三组 [2, 1, 3] 是连续的。

(3)最少调整次数:

只需要一次调整,比如将 8 和 9 互换,使第一组 [7, 8, 9] 成为连续的,或者调整第二组 [5, 6, 4] 使其符合目标排列。

在这里插入图片描述


🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2024 E卷 200分)

🏆本文收录于,华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

在这里插入图片描述

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值