PAT - B1008 数组元素循环右移问题(20分) https://pintia.cn/problem-sets/994805260223102976/problems/994805316250615808
大致思路:
自己的错误思路:按移动位数 M 将数据分组,每组每个元素循环右移 M 位(在所在组中相当于只右移一位)。- 暴力法(实测可以通过):每次循环右移一位,进行M次。
- 《算法笔记》方法:从
N - M
位开始枚举,直至N - M + d - 1
位截止(其中 d 为 N 和 M 的最大公约数;取此值的目的是避免重复移动元素,数学原理不清楚…)。对于枚举的每个位置,用指针逆向向前走(循环),以 M 为间隔将“逐个”元素循环右移“一”位,直到指针回到初始位置时停止。
通过代码(C/C++):
#include<stdio.h>
int gcd(int a, int b) { return b==0 ? a : gcd(b, a%b); } //求最大公约数
int main(){
int N, M;
scanf("%d %d", &N, &M);
int a[100];
for(int i=0;i<N;i++) scanf("%d", a+i);
M = M % N; // M可能大于N,做修正
/* 自己的高时间复杂度做法:每次循环右移一位,进行M次
for(int i=0;i<M;i++){
int temp=a[N-1];
for(int j=N-1;j>0;j--){
a[j]=a[j-1];
}
a[0]=temp;
}
*/
// 算法笔记解法:
if(M!=0){ //M=0时不用、也不能进入下面循环,直接进行后续输出
int i_end = N - M + gcd(N, M); //枚举的截止位下标(数学原理?),避免重复移动元素
for(int i=N-M;i<i_end;i++){
int pos=i, temp=a[i];
do{ //以M为间隔将各元素循环右移“一”位,直到指针回到初始位置
int next=(pos-M+N)%N;
a[pos] = next==i ? temp : a[next];
pos=next;
}while(pos!=i);
}
}
//
printf("%d", a[0]);
for(int i=1;i<N;i++) printf(" %d",a[i]);
return 0;
}
/*自己的早先错误做法,对于以下数据处理不正确(但通过了题目……)
8 3
1 2 3 4 5 6 7 8
#include<stdio.h>
int main(){
int N, M;
scanf("%d %d", &N, &M);
int a[100];
for(int i=0;i<N;i++) scanf("%d", a+i);
M = M % N; // M可能大于N,做修正
for(int i=0;i<M;i++){
int temp = a[i]; //暂存每次逆向循环第一个元素值
int j;
for(j=(i-M+N)%N;j>i;j-=M){ // 逆向循环移动一遍,间隔M
a[(j+M)%N] = a[j];
}
a[j+M] = temp; //本次循环最后一个元素
}
printf("%d", a[0]);
for(int i=1;i<N;i++) printf(" %d",a[i]);
return 0;
}
*/