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

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

文章目录

  • 题目描述
  • 题解
  • 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
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)

你可能感兴趣的:(算法竞赛快冲300题,算法)