说明:题目节选自《算法竞赛入门经典》(第二版),仅为了练习使用,跟着书中手敲代码,并添加自己的理解,仅此记录,我始终相信,当你开始不懂许多东西的时候,跟着书中弄懂作者的写的代码的思路,照着敲出来,敲多了,明白许多原理了,还有你会为了看懂它,查很多资料,然后当自己面对新的问题,脑子就有的调用了
备注:1.个人更喜欢C++,对于作者书中的沿用了C语言的部分,我会改成C++实现,如果涉及到效率问题,请参考原书
2.我会采用自己的命名习惯,以及添加注释,原书中的代码是针对竞赛的,我写代码是针对实际工程的,工程中变量最好能表明它的含义,以及必要的注释,方便其他人阅读,这也是习惯问题。
题目1:大理石在哪儿
思路:
1.接受用户多个输入,保存下来
2.对保存的数据排序
3.在已排序的数组中查找
代码实现:
#include <iostream>
#include <algorithm>
#include <array>
using namespace std;
const int arrSize = 10000;
int main()
{
//变量最好在声明时进行初始化
int marbleNum = 0; //大理石数量,即非负整数的数量
int questionNum = 0; //问题数量
int integerOfQuestion = 0; //问题中所问的整数
int ncase = 0; //第n波输入
//array对象会进行默认初始化吗?
array<int,arrSize> saveArray; //保存非负整数的数组
while((cin>>marbleNum>>questionNum) && marbleNum !=0)
//对while循环中cin解释:
//要保证有大理石,不然循环内的执行没有意义
{
cout<<"CASE# "<<++ncase<<endl;
//输入n个"大理石非负整数“
for(int i=0;i < marbleNum;i++)
cin>>saveArray[i];
//对输入的”大理石整数“进行排序
//sort的用法总结,以及这里的迭代器解释见代码后解释
sort(saveArray.begin(),saveArray.begin()+marbleNum);
//回答Q个问题
while(questionNum--)
{
//输入问题中要问的整数
cin>>integerOfQuestion;
//在数组中查找等于integerOfQuestion
//为什么最后要减去saveArray.begin()?
int intLocation = lower_bound(saveArray.begin(),saveArray.begin()+marbleNum,integerOfQuestion)-saveArray.begin();
//此时我们需要判断是否找到的是等于integerOfQuestion的数,
//因为lower_bound函数返回的是第一个大于或等于integerOfQuestion的位置,详细见下面解释
if(saveArray[intLocation] == integerOfQuestion)
cout<<integerOfQuestion<<" found at "<< intLocation+1<<endl; //题目中要求返回的是从1开始的位置,所以+1
else
cout<<integerOfQuestion<<" not found"<<endl;
}
}
}
eclipse下运行截图:
知识点详细用法补充:待补充
1.while下的cin
2.sort()函数和对应迭代器的使用
3.low_bound()函数
题目2:木块问题
且原题假设输入中,0<n<25
思路:
实现四个操作即可
1.可以看到该四个操作有公共的部分,我们可以提取出来写成函数,达到复用的效果
可以看到四个操作有重复的部分:归位木块,移动一个木块到另一个木块堆的顶部,移动一摞木块到另一个木块所在木块堆的顶部上,虽然归位本质也是移动,由于移动函数包括移动本身,而归位函数不包括移动本身,所以需要分开写
(1)归位函数:把某个木块上方的木块全部归位,输入:要归位的
(2)移动一个木块:输入:要移动的木块 输出:目标木块
(3)移动一摞木块:输入:要移动的一摞木块的起始木块 输出:目标木块
2.封装四个操作
3.在主函数里获取用户输入,用case选择相应操作
4.最后打印每个位置的木块列表
代码实现:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//函数声明
void back_above(int numOfBlock);
void moveOneBlock(int numMoveBlock,int numTargetBlock);
int findPile(int numOfBlock);
void moveBlocks(int beginMoveBlock,int numTargetBlock);
void print();
void moveOnto(int moveBlock,int targetBlock);
void moveOver(int moveBlock,int targetBlock);
void pileOnto(int moveBlock,int targetBlock);
void pileOver(int moveBlock,int targetBlock);
//因为0<n<25,所以我们让位置数组的长度为25
const int arrSize=25;
//用户输入的木块堆的个数
int input;
//定义一个每个元素为vector的数组,使用数组来代表每个位置,即pile[i]代表每个木块堆
//然后每个位置为vector,用于保存木块序号,因为移动让该位置的保存数据的数据结构不断变化,所以我们选用vector来保存
vector<int> pile[arrSize];
//归位函数:把某个木块上方的木块全部归位,输入:要归位的木块编号
//查找用户输入的木块编号的时间复杂度为O(n)
void back_above(int numOfBlock)
{
//循环遍历用户输入的n个木块堆
for(int i=0;i < input;i++)
{
//遍历一个木块堆中的木块
for(vector<int>::iterator it = pile[i].begin();it != pile[i].end();it++)
{
//如果找到了用户输入的木块
if(*it == numOfBlock)
{
//从后遍历木块,将每个木块插入到编号为它的木块堆中,并将它从现在的木块堆中删除
for(vector<int>::iterator iter = pile[i].end()-1;iter != it;iter--)
{
pile[*iter].push_back(*iter);
pile[i].pop_back();
//疑问:pop_back()会不会释放弹出元素原来所占的内存空间
}
break;
}
}
}
}
//移动一个木块:输入:要移动的木块 输出:目标木块 且要移动的木块上面已没有任何木块
void moveOneBlock(int numMoveBlock,int numTargetBlock)
{
//定义两个变量保存要移动木块和目标木块所在堆
int moveAtPile=findPile(numMoveBlock);
int targetAtPile=findPile(numTargetBlock);
//移动木块
pile[targetAtPile].push_back(numMoveBlock);
pile[moveAtPile].pop_back();
}
//找木块编号对应的木块堆的函数
int findPile(int numOfBlock)
{
//定义一个变量保存木块所在堆
int atPile=0;
//循环遍历木块堆找到要移动木块和目标木块所在堆
for(int i=0;i < input;i++)
{
//遍历一个木块堆中的木块
for(vector<int>::iterator it = pile[i].begin();it != pile[i].end();it++)
{
//如果找到了要移动的木块
if(*it == numOfBlock)
{
atPile = i;
return atPile;
}
}
}
return atPile;
}
//移动一摞木块:输入:要移动的一摞木块的起始木块 输出:目标木块
// 找到要移动木块和目标木块所在的堆
// 从要移动木块所在堆找要移动木块所在的位置
// 从要移动的木块开始移动到目标木块所在堆
void moveBlocks(int beginMoveBlock,int numTargetBlock)
{
//找到要移动木块和目标木块所在的堆
int moveAtPile=findPile(beginMoveBlock);
int targetAtPile=findPile(numTargetBlock);
for(vector<int>::iterator it = pile[moveAtPile].begin();it != pile[moveAtPile].end();it++)
{
if(*it == beginMoveBlock)
{
for(vector<int>::iterator iter = it;iter != pile[moveAtPile].end();it++)
{
pile[targetAtPile].push_back(*it);
}
for(vector<int>::iterator iter = it;iter != pile[moveAtPile].end();it++)
{
pile[moveAtPile].pop_back();
}
}
}
}
//打印结果列表
void print()
{
for(int i=0; i < input;i++)
{
cout<<i<<":";
for(int j=0;j < pile[i].size();j++)
{
cout<<" "<<pile[i][j]<<endl;
}
}
}
//封装四个操作
void moveOnto(int moveBlock,int targetBlock)
{
//将a和b上方的木块全部归位
back_above(moveBlock);
back_above(targetBlock);
//把a摞到b上
moveOneBlock(moveBlock,targetBlock);
}
void moveOver(int moveBlock,int targetBlock)
{
//将a上方的木块全部归位
back_above(moveBlock);
//把a放在b所在木块堆的顶部
moveOneBlock(moveBlock,targetBlock);
}
void pileOnto(int moveBlock,int targetBlock)
{
//将b上方的木块全部归位
back_above(targetBlock);
//把a以及上面的木块整体摞在b上面
moveBlocks(moveBlock,targetBlock);
}
void pileOver(int moveBlock,int targetBlock)
{
//把a以及上面的木块整体摞在b所在木块堆的顶部
moveBlocks(moveBlock,targetBlock);
}
int main()
{
cout<<"请输入木块堆的数目:"<<endl;
cin>>input;
}
elipse下运行结果:
未写完测试函数,待补充
经验总结:
题目3:安迪的第一个字典
思路:
找出不同的单词,并且进行排序,这就自然想到要用set
先获取到用户输入的文本串,再进行处理
将非法字符转化成空格,由于题目要忽略大小写,我们将所有字符都转化为小写
再插入set进行自动排序
将set中保存的单词打印出来即可
代码实现:
#include <iostream>
#include <string>
#include <set>
#include <sstream>
using namespace std;
//定义一个集合来保存每个单词
set<string> saveWord;
int main() {
string inputText;//用户输入的文本串
string wordBuffer;//用于sstream流分离单词时接受每个单词
//获取用户的输入的文本,直到遇到文件结束符或非法输入为止
while(cin >> inputText)
{
//遍历用户输入的文本字符串
for(unsigned int i=0;i < inputText.length();i++)
{
//如果是字母,转换为小写
if(isalpha(inputText[i]))
inputText[i] = tolower(inputText[i]);
else //不是字母,就是其他字符,转换成空格
inputText[i] = ' '; //刚开始输入的是“ ”,报错,因为inputText本身是字符串,对它使用[]运算符返回的是字符,所以只能用‘’
}
//用stringstream将文本串分割成单个单词
//原理见补充部分
stringstream word(inputText);
while(word >> wordBuffer)
{//将单个单词插入集合中
saveWord.insert(wordBuffer);
}
}
//打印set集合中顺序保存的字符串
for(set<string>::iterator it=saveWord.begin();it != saveWord.end();it++)
cout<<*it<<endl;
}
eclipse下运行效果图:
知识点详细用法补充:待补充
1.isalpha()
2.tolower()
3.stringstream()