PAT B1008——数组元素循环右移

方法1

不进行重排,直接从指定位置循环输出。

方法2

按照题目要求用最少交换次数重排。

具体算法:

  1. 选择第i号数字作为起始位(索引从0开始)。将起始位的数字拿出储存,然后剩下数字依次填补上一个空缺,直到返回起始位。
  2. 由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;
}
posted @ 2019-07-08 14:03  Ivan_Luo  阅读(200)  评论(0编辑  收藏  举报