木块问题:
描述:
从左到右有n个木块,编号为0~n-1,要求模拟以下4种操作(下面的a和b都是木块编
号)。
move a onto b:把a和b上方的木块全部归位,然后把a摞在b上面。
move a over b:把a上方的木块全部归位,然后把a放在b所在木块堆的顶部。
pile a onto b:把b上方的木块全部归位,然后把a及上面的木块整体摞在b上面。
pile a over b:把a及上面的木块整体摞在b所在木块堆的顶部。
遇到quit时终止一组数据。a和b在同一堆的指令是非法指令,应当忽略。
例:
Sample Input
10
move 9 onto 1
move 8 over 1
move 7 over 1
move 6 over 1
pile 8 over 6
pile 8 over 5
move 2 over 1
move 4 over 9
quit
Sample Output
0: 0
1: 1 9 2 4
2:
3: 3
4:
5: 5 8 7 6
6:
7:
8:
9:
———————————————————————————————————————————
题目分析:
刚开始看到题目时,一脸懵逼,完全不懂题目什么意思,后来在看到博主[蓝冰lanbing]的分析 +自己多看几遍题目,细细品味才看懂。
10
move 9 onto 1
move 8 over 1
move 7 over 1
move 6 over 1
pile 8 over 6
pile 8 over 5
move 2 over 1
move 4 over 9
再回看下题目:
1、从左到右有n个木块,编号为0~n-1
意思如图(假设有10个木块):
最开始,10个木块分成10堆,编号从0~9.
2、move 9 onto 1
把9和1上方的木块全部归位,然后把9摞在1上
3、第三点还有注意的是:over b是找到b所在的那堆,并放在那堆的上面
//在这里,我只想说一句,对于语言文字理解能力差的我,看懂题目真的不容易啊
声明:参考CSDN博主「蓝冰lanbing」
原文链接:https://blog.csdn.net/weixin_43827530/article/details/100671692
代码部分:
注意:这里输入一共有4种指令,但如果完全独立地处理各指令,代码就会变得冗长而且易错。更好的方法是提取出指令之间的共同点,编写函数以减少重复代码。
1、找木块a所在的pile和height ,以引用形式返回调用者
void find_block(int a, int& p, int& h) {
for (p = 0;p < n; p++)
for(h =0;h<pile[p].size();h++)
if (pile[p][h] == a) return;
}
2、把第P堆高度为h的木块上方的所有木块移回原位
void clear_above(int p, int h) {
for (int i = h + 1; i < pile[p].size();i++) {
int b = pile[p][i];
pile[b].push_back(b); //把木块b放回原位
}
pile[p].resize(static_cast<std::vector<int, std::allocator<int>>::size_type>(h) + 1); //pile只应保留下标0~h的元素
}
//(因为我用的是vs2022版,所以加了static_cast<std::vector<int, std::allocator<int>>::size_type>,其他版本可能只需写pile[p].resize(h+1)即可)
3、把第p堆高度为h及其上方的木块整体移动到p2堆的顶部
void pile_onto(int p, int h, int p2) {
for (int i = h;i < pile[p].size();i++) {
pile[p2].push_back(pile[p][i]);
pile[p].resize(h);
}
}
4、打印函数
void pile_onto(int p, int h, int p2) {
for (int i = h;i < pile[p].size();i++) {
pile[p2].push_back(pile[p][i]);
pile[p].resize(h);
}
}
总代码:
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
const int maxn = 30;
int n;
vector<int> pile[maxn]; //每一个pile[i]都是都是一个vector
//找木块a所在的pile和height ,以引用形式返回调用者
void find_block(int a, int& p, int& h) {
for (p = 0;p < n; p++)
for(h =0;h<pile[p].size();h++)
if (pile[p][h] == a) return;
}
//把第P堆高度为h的木块上方的所有木块移回原位
void clear_above(int p, int h) {
for (int i = h + 1; i < pile[p].size();i++) {
int b = pile[p][i];
pile[b].push_back(b); //把木块b放回原位
}
pile[p].resize(static_cast<std::vector<int, std::allocator<int>>::size_type>(h) + 1); //pile只应保留下标0~h的元素
}
//把第p堆高度为h及其上方的木块整体移动到p2堆的顶部
void pile_onto(int p, int h, int p2) {
for (int i = h;i < pile[p].size();i++) {
pile[p2].push_back(pile[p][i]);
pile[p].resize(h);
}
}
void print() {
for (int i = 0;i < n;i++) {
printf("%d:", i);
for (int j = 0;j < pile[i].size();j++) {
printf(" %d", pile[i][j]);
}
printf("\n");
}
}
int main()
{
int a, b;
cin >> n;
string s1, s2;
for (int i = 0;i < n;i++)
pile[i].push_back(i);
while (cin >> s1 >> a >> s2 >> b ) {//&& s1!= "quit"
int pa, pb =0, ha, hb=0;
find_block(a, pa, ha);
find_block(b, pb, hb);
if (pa == pb) continue;//非法指令
if (s2 == "onto") clear_above(pb, hb);
if (s1 == "move") clear_above(pa, ha);
pile_onto(pa, ha, pb);
}
print();
return 0;
}
最后再对知识点进行一个补充:
知识补充:
(1)static_cast显性类型强制转换,和C语言学习时的显性意义一样,但是编译器会对此类型转换进行检查
一般来说,编译器隐式执行的任何类型转换都可以由static_cast显式完成。static_cast可以用来将枚举类型转换成整型,或者整型转换成浮点型。也可以用来将指向父类的指针转换成指向子类的指针。做这些转换前,你必须确定要转换的数据确实是目标类型的数据,因为static_cast不做运行时的类型检查以保证转换的安全性。也因此,static_cast不如dynamic_cast安全。对含有二义性的指针,dynamic_cast会转换失败,而static_cast却直接且粗暴地进行转换,这是非常危险的。
还有要注意的是,他不能转换掉expression的const、volatile、或者__unaligned属性,同样也不能用来去掉static属性。
C++中的sta tic_cast执行非多态的转换,用于代替C中通常的转换操作。对于我们的static_cast转换符,他不仅可以应用到指针和引用上,而且还可以应用到基础数据结构和对象上。
--------------------------------------------
声明:来自CSDN博主「Citronnelle2」
原文链接:https://blog.csdn.net/zhouwei1221q/article/details/44978361
(2)std::allocator
标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。使用allocator通常会提供更好的性能和更灵活的内存管理能力。
new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。我们分配单个对象时,通常希望将内存分配和对象初始化组合在一起。因为在这种情况下,我们几乎肯定知道对象应有什么值。当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象的创建操作(同时付出一定开销)。一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费。
标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来。它提供一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。类似vector,allocator是一个模板。为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置。
————————————————
声明:来自CSDN博主「fengbingchun」
原文链接:https://blog.csdn.net/fengbingchun/article/details/78943527