《算法竞赛·快冲300题》每日一题:“排列变换”

文章介绍了《算法竞赛·快冲300题》中的一道排列变换问题,提供了C++、Java和Python三种语言的解题代码。问题涉及将一个排列转换成另一个排列,最小化移动次数,解题策略是通过贪心法分析数字相对位置。代码示例展示了如何找到数字的新位置并计算移动次数。
摘要由CSDN通过智能技术生成

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。


排列变换” ,链接: http://oj.ecustacm.cn/problem.php?id=1812

题目描述

【题目描述】
  给定一个长度为n的排列a,需要将这个排列变成b。
  每次可以选择一个数字往左移若干个位置。
  请求出最小移动次数。
【输入格式】
  第一行为正整数n,1≤n≤100000。
  第二行为n个整数,表示排列a。
  第三行为n个整数,表示排列b。
【输出格式】 输出一个数字表示答案。
【输入样例】

5
5 1 3 2 4
4 5 2 1 3

【输出样例】

2

题解

  本题要求把原序列a重新排列变为序列b,最简单的办法是:直接把a中的数一个个放到b中对应的位置。这样能得到结果,但显然移动次数不是最小的。为了得到最小移动次数,需要找出哪些数字必须移动,哪些不用移动。这跟数字的相对位置有关,例如样例中的“5 1 3”,它们在a和b中的相对位置一样,不用移动;而“2”肯定需要移动,因为在a中位于“1”前,在b中位于“1”后。
  分析a中每个数在b中的新位置,那些相对位置不变的不用移动,那些相对位置变化的需要移动。例如样例中a=“5 1 3 2 4”,要变为b=“4 5 2 1 3”,把a中数字的新位置记为位置序列c =“2 4 5 3 1”,其中a的第一个数“5”移动到b的第“2 ”位置,第二个数“1”移动到b的第“4 ”位置,等等。c =“2 4 5 3 1”是a的新位置,显然,“2 4 5”这3个位置的数字“5 1 3”在a和b中的相对位置不变,不用移动,而“3 1”位置上的两个数字“2 4”需要左移到正确的位置。
  经过上述分析,本题的最终解决方案是:把排列a根据排列b的要求,得到位置序列c;分析c中每个数,如果比左边的小就移动到左边,否则不用移动。这是贪心法的思路。计算复杂度O(n)。
【笔记】 掌握数字的位置变换。

C++代码

  本题非常重要,它能帮助读者深刻理解数组下标的应用。下面代码的8、9、10行完成了从 a、b得到 c的过程,借助了下标数组id[]。这是一个常见的技巧,请一定掌握。
  本题的数据比较简单,是1 ~ n的n个数,可以当成数组的下标使用。如果是n个随机数,可以用离散化技巧变为1~n范围的数。

 #include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N], c[N];
int id[N];                                     //id[x]:  数字x在b数组中的下标
int main(){
    int n;    cin >> n;
    for(int i=1; i<=n; i++)  cin >> a[i];
    for(int i=1; i<=n; i++)  cin >> b[i], id[b[i]] = i;   
    for(int i=1; i<=n; i++)  c[i] = id[a[i]];  //把a数组变成对应b数组的下标,得到位置序列c
    int ans=0, Max=0;
    for(int i=1; i<=n; i++){
        if(Max > c[i])  ans++;                 //只要c[i]左边有一个比c[i]大,则答案++
        Max = max(Max, c[i]);
    }
    cout<<ans<<endl;
    return 0;
}

Java代码

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[] a = new int[n + 1];
        int[] b = new int[n + 1];
        int[] c = new int[n + 1];
        int[] id = new int[n + 1];             //id[x]:  数字x在b数组中的下标
        for (int i=1; i<=n; i++)   a[i] = input.nextInt();        
        for (int i=1; i<=n; i++) {
            b[i] = input.nextInt();
            id[b[i]] = i;
        }
        for (int i=1; i<=n; i++)  c[i] = id[a[i]];  //把a数组变成对应b数组的下标,得到位置序列c      
        int ans = 0;
        int max = 0;
        for (int i = 1; i <= n; i++) {
            if (max > c[i])   ans++;                //只要c[i]左边有一个比c[i]大,则答案++   
            max = Math.max(max, c[i]);
        }
        System.out.println(ans);
    }
}

Python代码

n = int(input())
a = [0] + list(map(int, input().split()))           #不用a[0],从a[1]开始
b = [0] + list(map(int, input().split()))           #不用b[0],从b[1]开始
id = [0] * (n + 1)                                  #id[x]:  数字x在b数组中的下标
for i in range(1, n + 1):   id[b[i]] = i
c = [0] + [id[a[i]] for i in range(1, n + 1)]       #把a数组变成对应b数组的下标,得到位置序列c
ans = 0
max_val = 0
for i in range(1, n+1):
    if max_val > c[i]:  ans += 1                    #只要c[i]左边有一个比c[i]大,则答案+1
    max_val = max(max_val, c[i])
print(ans)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

罗勇军

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

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

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

打赏作者

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

抵扣说明:

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

余额充值