UVa120 煎饼(选择排序思想)
题目背景
给你一迭薄煎饼,请你写一个程式来指出要如何安排才能使这些薄煎饼由上到下依薄煎饼的半径由小到大排好。所有的薄煎饼半径均不相同。
要把薄煎饼排好序需要对这些薄煎饼做翻面(flip)的动作。方法是以一抹刀插入一迭薄煎饼中,然后做翻面的动作(也就是说在抹刀上面的薄煎饼经翻面后,会依相反的次序排列)。若一迭共有\(n\)个薄煎饼,我们定义最底下的薄煎饼的位置为1,最上面的薄煎饼位置为\(n\)。当抹刀插入位置为\(k\)时,代表从位置\(k\)到位置\(n\)的薄煎饼要做翻面的动作。
一开始时,这迭薄煎饼随意堆放,并以半径大小来表示。例如:以下3迭薄煎饼(最左边那一迭8是最上面一个薄煎饼的半径)
8 7 2
4 6 5
6 4 8
7 8 4
5 5 6
2 2 7
对最左边那迭薄煎饼,如果我们把抹刀插在位置3(就是半径为7的那块薄煎饼的下面)的地方做翻面,就会得到中间那迭,如果我们再把抹刀插在位置1(就是半径为2的那块薄煎饼的下面)的地方做翻面,就会得到最右边那迭。
Input
每组测试资料一行,内容为这一迭薄煎饼一开始的状态。每列开始的整数(介于1到100之间)代表位于最上方薄煎饼的半径,依此类推。薄煎饼的数目介于1到30之间。请参考输入样例。
Output
对每一组测试资料输出2列。第一列为原来那迭薄煎饼。第2列则为要使这迭薄煎饼由小到大排列所做的翻面的动作。数字代表抹刀所插入的位置。(0代表已完成)。如果已经排好了,则不需再有翻面的动作。请参考输出样例。
输入输出样例
输入
1 2 3 4 5
5 4 3 2 1
5 1 2 3 4
输出
1 2 3 4 5
0
5 4 3 2 1
1 0
5 1 2 3 4
1 2 0
题解
如果我们设题目输入数组为\(A\),长度为\(n\)。那么我们需要注意的一点是,\(A[0]\)对应一叠煎饼的顶端,\(A[n-1]\)对应一叠煎饼的底部。如下图所示:
故要想将数组倒数第\(i\)个位置及其之前的元素(即\(A[0:n-i]\))翻转,我们输出的抹刀插入位置为\(i\)。
采用选择排序的思想,按从大到小的顺序依次把元素移动到正确位置,一共\(n\)轮,其中第\(i\)轮(\(i=1,2,...,n\))排序把数组\(A[0:n-i]\)中最大的元素移动到下标\(n-i\)处(如果本身就在下标\(n-i\)处则跳过)。
不过这里我们只能翻转一个序列,故我们需要用到一个技巧,就是分两步走:
① 先将\(A[0:n-i]\)中最大的元素“翻”到最左端的下标\(0\)处(如果本身就在下标\(0\)处则跳过)。
② 然后再将它“翻”到正确位置即下标\(n-i\)处。
设数组\(A[0:n-i]\)中数组最大元素的下标为\(maxs\),前者需要将\(A[0:maxs]\)(即倒数第\(n - maxs\)个位置及其之前的元素)翻转,此时输出的抹刀插入位置应为\(n-maxs\);后者需要将\(A[0:n-i]\)(即倒数第\(i\)个位置及其之前的元素翻转,此时输出的抹刀插入位置应为\(i\)。
根据以上的分析,我们的算法如下:
'''
Descripttion: 选择排序思想
Version: 1.0
Author: ZhangHongYu
Date: 2021-08-12 15:54:27
LastEditors: ZhangHongYu
LastEditTime: 2021-08-28 22:01:07
'''
while True:
try:
A = list(map(int, input().strip().split()))
t = A.copy()
res = []
n = len(A)
# 排序趟数
for i in range(1, n+1):
# 获取A[0:n-i]中最大元素的下标
max_v = max(A[: n - i + 1])
maxs = A[: n - i + 1].index(max_v)
# 如果最大元素还未翻到下标n-i处
if maxs != n - i:
# 如果最大元素还未翻到下标0处
if maxs != 0:
# 将A[0:maxs]翻转
A[: maxs + 1] = list(reversed(A[: maxs + 1]))
# 此时抹刀插入位置应为 n - maxs
res.append(n - maxs) #
# 将A[0:n-i]翻转
A[: n - i + 1] = list(reversed(A[: n - i + 1]))
# 此时抹刀插入位置应为i
res.append(i)
res.append(0)
print(" ".join(map(str, t)))
print(" ".join(map(str, res)))
except EOFError:
break