紫书刷题进行中,题解系列【GitHub|CSDN】
例题8-1 UVA120 Stacks of Flapjacks(24行AC代码)
题目大意
给定一叠具有半径大小的煎饼,从下到上位置编号为1n,有翻转操作flip(i),表示将第in个位置的元素翻转。
现问如何用最少次数将所有煎饼从上到下按半径升序排列?
思路分析
有点像汉诺塔,不过这个是分治法的例题,通过研究样例,可以发现算法:
1. 从底到顶开始枚举位置j
2. 在[j,0]选取最大值位置pos
3. 若pos不为j,进入步骤4
4. 若pos不为顶部,则翻转[0,pos]; 翻转[0,j]
本质是现将当前最大元素翻转到顶部,再将其翻转到正确位置。翻转可用stl的reverse
实现
关于最大值选取,有两种方式
max_element
:stl算法库/手动实现- 将输入数组降序排列,枚举该数组时,查找相应元素即可
AC代码(C++11)
max_element
#include<bits/stdc++.h>
using namespace std;
int pc[32], n;
int main() {
string line;
while (getline(cin, line)) {
cout <<line <<endl; // 输出原序列
stringstream input(line); n = 0;
while (input >>pc[n]) n ++; // 分割得到所有数字
for (int i=0; i < n-1; i ++) { // 从大到小依次归位煎饼
int pos = max_element(pc, pc+n-i) - pc; // 找到最大值的下标
if (pos != n-i-1) { // 未正常归位
if (pos != 0) { // 不在栈顶
printf("%d ", n - pos); // 输出位置
reverse(pc, pc+pos+1); // 翻转到最上层
}
printf("%d ", i+1); // 第二次输出
reverse(pc, pc+n-i); // 翻转到正确位置
}
}
puts("0");
}
return 0;
}
sort+find
#include<bits/stdc++.h>
using namespace std;
int pc[32], tpc[32], n;
int main() {
string line;
while (getline(cin, line)) {
stringstream input(line); n = 0;
while (input >>pc[n]) n ++; // 分割得到所有数字
for (int i=0; i < n; i ++) printf("%d ", pc[i]); puts(""); // 输出原序列
memcpy(tpc, pc, sizeof(pc)); // 拷贝pc数组
sort(tpc, tpc+n, [](int a, int b) {return a > b;}); // 降序排列,处理最大值问题
for (int i=0; i < n-1; i ++) { // 从大到小依次归位煎饼
int pos = find(pc, pc+n-i, tpc[i]) - pc; // 找到tpc[i]在pc中的下标
if (pos != n-i-1) { // 未正常归位
if (pos != 0) { // 不在栈顶
printf("%d ", n - pos); // 输出位置
reverse(pc, pc+pos+1); // 翻转到最上层
}
printf("%d ", i+1); // 第二次输出
reverse(pc, pc+n-i); // 翻转到正确位置
}
}
puts("0");
}
return 0;
}