原问题: https://www.patest.cn/contests/pat-b-practise/1008
一个数组A中存有N(N>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M>=0)个位置,即将A中的数据由(A0A1……AN-1)变换为(AN-M …… AN-1 A0 A1……AN-M-1)(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
输入格式:每个输入包含一个测试用例,第1行输入N ( 1<=N<=100)、M(M>=0);第2行输入N个整数,之间用空格分隔。
输出格式:在一行中输出循环右移M位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。
输入样例:6 2 1 2 3 4 5 6输出样例:
5 6 1 2 3 4
首先考虑 1 2 3 4 5 后移2步情况,我们发现只需要将固定的某个位置的数字不断和它应当去的位置的数字交换即可,即:
假设固定第一个数字,1 应当去 3 的位置,互换变为 3 2 1 4 5,此时第一位变为 3
继续移动第一位的数字,3 应当去 5 的位置,互换后为 5 2 1 4 3……
重复4次后即可获得想要的结果
尝试题目样例,很快会发现问题:
移动1次: 3 2 1 4 5 6
移动2次: 5 2 1 4 3 6
移动3次:5 2 1 4 3 6
出现环套,不难发现其实6个数分为2组,1 3 5一组,2 4 6一组,每组移动2次,一共4次即可得到答案
此时考虑当中的统一的规律,即发现是:最大公约数;数字个数和移动距离的最大公约数即为可分的组数
对于N个数字,移动M个位置, CYCLE=最大公约数(N,M) 即为分组数,最少需要移动 N-CYCLE 次
此时,问题归结为求解 N和M 的最大公约数CYCLE,然后分CYCLE次循环移动各组内数字;每个组内移动 N/CYCLE-1 次即可
代码如下:
def highest_common_divissor(bigger, smaller): if bigger < smaller: bigger, smaller = smaller, bigger common_divissors = [smaller] #把smaller放进去后便可将下面的for循环减少一半步数,提高效率 up_limit = int(smaller / 2) for i in range(up_limit, 0, -1): if smaller % i == 0: common_divissors.append(i) for common_divissor in common_divissors: #只在smaller的约数里测试,从最大到小寻找,然而这样会导致很多时候要从smaller找到1才有答案 if bigger % common_divissor == 0: return common_divissor parameter = input() parameter = parameter.split(' ') count = int(parameter[0]) #读取N move = int(parameter[1]) #读取M numbers = input() numbers = numbers.split(' ') #要处理的数字 cycle = highest_common_divissor(count, move) #最大公约数,分组数目 for j in range(cycle): #每组内循环交换 origin_last = j + 1 #每个分组内数据交换开始位置 step = int(count/cycle) #每组内需要交换 step-1 次 for i in range(1, step): origin_next = (origin_last + move) % count #计算将要交换数字的目的位置,注意此处数组编号从1开始计算 numbers[j], numbers[origin_next-1] = numbers[origin_next-1], numbers[j] #每当一组数据交换结束,j+1后开始下一组数据的交换 origin_last = origin_next #将刚被交换过来数字的原本位置更新,用于下一次交换 message = '' #数据输出,不必多数 for number in numbers: message += number message += ' ' print(message.rstrip())按照上述思路写出代码,轻松通过了题目示例,然而提交后出现了问题,有一个 返回非零 的测试,扣2分
python不需要像C或者C++那样在main函数末尾 return 0,那么问题出在哪里了;检查了数据格式等等均无问题
这种时候不需要再怀疑语法或者流程了,大部分测试通过说明这些都没问题
重新审题,看到 0<=N<=100, M>=0 M可以取0,但是我的最大公约数算法并没有考虑到M为0情况,因此修改cycle求解:
if move > 0: cycle = highest_common_divissor(count, move) else: cycle = 0果然,就是这个出了问题,测试全部通过
当然,也可以选择将这个修改放到最大公约数函数里
总结:看到各种奇怪的测试错误时,好好审题,大概率是在细节上遗漏了什么