《算法竞赛·快冲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)