原题地址https://www.patest.cn/contests/pat-b-practise/1008
这道题很早之前就用暴力输出AC过了,可是本题解法众多,这里介绍两种方法。第一种方法来源于网上的博客,非常有趣。第二种方法也不是本人原创,不敢夺人之功。
第一种:翻转法
这是一种很有技巧性的解法,经过三次翻转即可得到结果。我们设数组长度为N,右移位数为M。
1. 首先将区间(以0位首位), 【N- M,N - 1】进行翻转。
2.再将区间【0,(N - M - 1)】进行对称翻转。
3.最后将整个区间进行对称翻转。
比如:N = 8, M = 3,数组为1 2 3 4 5 6 7 8
第一步,将 6 7 8三个数进行翻转, 数组变为1 2 3 4 5 8 7 6
第二步,将1 2 3 4 5 五个数进行对称翻转,数组变为 5 4 3 2 1 8 7 6
第三步,将整个数组进行对称翻转,数组变为6 7 8 1 2 3 4 5
放一波C艹实现
#include <cstdio>
const int MAXN = 110;
void reverse(int a[], int left, int right)
{
int temp; //用于接收临时变量
for(int i = left;i <= (left + right)/2 ;i++) // 中心翻转
{
temp = a[i];
a[i] = a[right + left - i];
a[right + left -i] = temp;
}
}
int main()
{
int N, M, iInput[MAXN];
while(~scanf("%d %d", &N, &M))
{
for(int i = 0;i < N;i++)
{
scanf("%d", &iInput[i]); // 输入
}
M %= N; //M有大于N的情况
reverse(iInput, N - M, N - 1);
reverse(iInput, 0, N - M - 1);
reverse(iInput, 0, N - 1);
for(int i = 0;i < N;i++)
{
printf("%d%c", iInput[i], (i != N - 1)? ' ':'\n');
}
}
return 0;
}
上述代码的时间复杂度为O(n),还不错。
第二种:循环法
这种方法来源于《算法笔记:上机训练实战指南》,使用了一种比较特殊的算法。下面介绍一下实现方法。
以N = 6,M = 2,数组为1 2 3 4 5 6为例。
还是以0为首位,将第(N - M)位的数字拿出,保存在temp变量中,这时数组变为
1 2 3 4 _ 6
然后,将应该结果中放在(N - M)位置的数组,本例中即为 3 和第( N - M)位进行交换,此时,数组变为
1 2 _ 4 3 6
然后继续这一过程,
_ 2 1 4 3 6
这时,下一次将空缺位左移会回到初始位置,所以不再移动,然后将temp变量填入空缺位,数组变为
5 2 1 4 3 6
然后,再把第(N - M + 1)位保存到temp,继续这一过程,直到第(N - 1)位完成。
但是这种方法有缺陷,各位可以手动模拟一下,当N = 8,M = 3,这时其实当第一次循环完成后数组就已经是正确答案了,继续循环反而会导致错误。
针对这个问题,书中也给出了解决方法。设d为N 和 M的最大公约数,那么只要从N - M位开始循环,直到N - M + d - 1位结束就可以。
下面又是一波C艹实现
#include <cstdio>
const int MAXN = 110;
int gcd(int a,int b) // 求最大公约数
{
if(b == 0) return a;
else return gcd(b, a%b);
}
int main()
{
int N, M, iInput[MAXN];
while(~scanf("%d%d",&N, &M))
{
for(int i = 0;i < N;i++)
{
scanf("%d",&iInput[i]);
}
M %= N;
if(M != 0)
{
int d = gcd(N, M);
for(int i = N - M;i <= N - M + d - 1;i++)
{
int temp = iInput[i]; //记录temp变量;
int pos = i, next; // pos为此时此刻正在处理的变量,即为空缺位, next为将要和空缺位进行交换的位置
do
{
next = (pos - M + N) % N; //计算将要进行交换的位置 ,加N是如果移动到数组小于M的位置时再从右边开始
if(next == i) //如果回到了初始位置
iInput[pos] = temp;
else
iInput[pos] = iInput[next]; //如果下一个位置不是初始位置,那么将下一个位置的数字填入空缺位
pos = next; // 继续下一个位置
} while(pos != i);
}
}
for(int i = 0;i < N;i++)
{
printf("%d%c",iInput[i], (i != N - 1)?' ':'\n');
}
}
return 0;
}
以上两种方法都已经AC,可放心食用。
(第一篇博客,若有不妥之处希望各位读者多多指教)