把目标顺序重标号后,统计初始排列中不是前缀最大值的牛,它们必须被向左移动。
OJ: usaco
题目 ID: 1204
难度:普及-
标签:贪心逆序排列usaco
日期: 2026-07-11 17:41
题意
给定初始排列 a 和目标排列 b。
一次操作可以选择一头牛,把它向左移动任意若干个位置。
求把 a 变成 b 的最少操作次数。
思路
先看一个
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 17:41
* update_at: 2026-07-11 17:42
*/
// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n;
int a[MAXN], b[MAXN];
int pos_in_b[MAXN];
int relabel[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
pos_in_b[b[i]] = i;
}
for (int i = 1; i <= n; i++) {
relabel[i] = pos_in_b[a[i]];
}
int ans = 0;
for (int j = 1; j <= n; j++) {
bool need_move = false;
for (int i = 1; i < j; i++) {
if (relabel[i] > relabel[j]) {
need_move = true;
}
}
if (need_move) {
ans++;
}
}
cout << ans << '\n';
return 0;
}先把目标排列 b 重标号成 1,2,...,N。也就是说,牛 b[i] 的新编号是 i。
这样目标就是把重标号后的 a 排成递增顺序。
考虑当前位置的牛 a[j]。如果它左边存在一头牛 a[i],满足:
text
i < j 且 a[i] > a[j]那么这两头牛在目标顺序中是反的。由于操作只能把被选中的牛向左移动,所以必须选择当前这头牛 a[j] 至少一次,才能改变这对相对顺序。
因此,一头牛是否必须移动,只取决于它左边是否出现过比它更大的重标号。
用 max_so_far 维护左边最大值:
- 如果当前
order > max_so_far,它是新的前缀最大值,不需要移动; - 否则左边存在更大的值,它必须移动,答案加一。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 17:41
* update_at: 2026-07-11 17:42
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n;
int a[MAXN], b[MAXN];
int pos_in_b[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
pos_in_b[b[i]] = i;
}
int ans = 0;
int max_so_far = 0;
for (int i = 1; i <= n; i++) {
int order = pos_in_b[a[i]];
if (order > max_so_far) {
max_so_far = order;
} else {
ans++;
}
}
cout << ans << '\n';
return 0;
}复杂度
只扫描排列常数次。
时间复杂度为
总结
本题的关键是把目标排列重标号为递增序列。
重标号后,必须移动的牛正是那些左边存在更大值的牛,也就是不是前缀最大值的元素。