方法1
不进行重排,直接从指定位置循环输出。
方法2
按照题目要求用最少交换次数重排。
具体算法:
- 选择第i号数字作为起始位(索引从0开始)。将起始位的数字拿出储存,然后剩下数字依次填补上一个空缺,直到返回起始位。
- 由N-M至N-M+d-1依次作为起始位,进行步骤1。结束后即完成重排。(注:d为MN的最大公约数)
- 例如:假设N=6,M=4
第一次取出索引为2的数字3,然后依次填补空缺。
1 2 X 4 5 6
1 2 5 4 X 6
X 2 5 4 1 6 //下次移动的数字将为2号位,所以停止
3 2 5 4 1 6 //放回3
第二次第一次取出索引为3的数字4,然后依次填补空缺。
3 2 5 X 1 6
3 2 5 6 1 X
3 X 5 6 1 2 //下次移动的数字将为3号位,所以停止
3 4 5 6 1 2 //放回4
完成
分析
其实不必非要从N-M开始至N-M+d-1结束,依次选取连续的d个数作为起始位即可。
-
因为每次步骤1的跨度为K(设K为最大公倍数),K/M即为每次步骤1所重排的数字个数。(因为移动的距离为4,而返回起始位需要移动距离为6的倍数,所以完成一次步骤1一共跨越了12个数,有3个数字被重排)
-
每个数字经历一次重排就可到达指定位置,且一共有N个数字需要重排,N/(K/M)即为需要进行步骤1的次数。(MN/K即d,因此进行d次步骤1即可)
-
易知连续地选取起始位即可使每个数字都被重排。
参考代码
#include <cstdio>
int gcd(int a, int b) {
if(b == 0) return a;
else return gcd(b, a % b);
}
int main() {
int a[110];
int n, m, temp, pos, next;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
m = m % n;
if(m != 0) {
int d = gcd(m, n);
for(int i = n - m; i < n - m + d; i++) {
temp = a[i];
pos = i;
do {
next = (pos - m + n) % n;
if(next != i) a[pos] = a[next];
else a[pos] = temp;
pos = next;
}while(pos != i);
}
}
for(int i = 0; i < n; i++) {
printf("%d", a[i]);
if(i < n - 1) printf(" ");
}
return 0;
}