一、题意
输入1 ~ n 的一个排列(n<=500),每次可以交换两个整数,输出至少要用多少次交换,就可以把排列变成一个1 ~ n 的环状排列。
二、解析
由于n比较小,因此暴力模拟法即可。
首先枚举目标排列,总共 2 * n 种(以每一个下标作为开头1、顺时针和逆时针),然后对于每一个目标排列,计算从原始排列a[maxn]到目标排列需要交换的次数。然后取所有次数的最小值即为答案。
为了快速定位原始排列中某些元素的位置,我们可以用一个额外的pos[maxn]来记录元素位置,然后每次模拟交换元素时,同时交换相应的pos数组中的值即可。
三、代码
#include <iostream> #include <vector> #include <cmath> #include <cstring> using namespace std; const int maxn = 500 + 5, INF = 1 << 30; int n, a[maxn]; int solve(int offset, int direct) { static int temp[maxn], pos[maxn]; memcpy(temp, a, sizeof(int) * (n + 1)); for(int i = 1; i <= n; i ++) pos[temp[i]] = i; int res = 0; for(int i = 0; i < n; i ++) { int num = (offset + direct * i + n) % n + 1, pos1 = pos[num], pos2 = i + 1; if(pos1 != pos2) { // 模拟时temp数组和pos数组都要进行交换 res ++; swap(temp[pos1], temp[pos2]); swap(pos[temp[pos1]], pos[temp[pos2]]); } } return res; } int main() { while(cin >> n && n) { for(int i = 1; i <= n; i ++) cin >> a[i]; int ans = INF; for(int i = 0; i < n; i ++) ans = min(min(ans, solve(i, 1)), solve(i, -1)); // 注意顺逆时针都要考虑 cout << ans << endl; } }
四、归纳
- 还是那句话:时间允许的情况下,算法怎么暴力怎么来
- 维护pos数组是一种很常见的技巧