一、题目
老鼠找食物,但是回家的时候找到最短路输入是x-y,x是1234其中的一个,代表四个方向,y是向这个方向走的距离。比如:
格式数字-数字
1-2 表示,向上走两部
2-3 向下走3步
3-1 向左走1步
4-2 向右走2步
0-0 表示找到了
然后返回的时候,找到最短路径
然后要求给他找回头路,把重复的路给去掉
题目首先规定四个方向:1、2、3、4分别代表上下左右
输入序列形式为1-3 3-4 1-4... ,前一个数字代表方向,后一个数字代表前进距离,以0-0为结束,结束则代表老鼠找到了食物。
老鼠在碰到死路时会原路返回到分叉路,探索下一个方向。
需要求解老鼠原路返回的最佳路径,以 2-3 4-2… 等作为输出。最佳路径的描述是“不走回头路”,即没有折返过程即可。
二、分析
最开始想利用二维数组来模拟一个地图,但是根据题目看,这个有左右上下,那就得有负数的坐标,并不好实现。
考虑到每行动一次,都要记录两个属性:方向和步数,于是考虑定义一个结构体Action,封装这两个数据。
考虑到输入数据的形式,需要输入字符串序列,因此还要接收和处理字符串。
需要寻找最短路径,考虑通过vector记录每一个动作,先遍历找到最短路径,再按最短路径折返。考虑到折返的需要,因此使用堆栈记录最短路径,然后出栈刚好就是返回路径。
三、测试样例
示例1:
输入:
1-1 3-1 1-1 2-1 4-2 1-2 4-1 1-1 2-1 3-1 1-1 0-0
输出:
2-3 3-1 2-1
示例2:
输入:
1-10 0-0
输出:
2-10
示例3:输入:
1-1 4-1 1-1 4-1 1-1 3-1 1-1 2-1 4-2 1-1 0-0
输出:
2-1 3-1 2-1 3-1 2-1 3-1 2-1
四、代码实现
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
using namespace std;
typedef struct Action {
int direction;
int steps;
}Action;
void print_vec(vector<Action> vec);
vector<Action> find_shortest_path(vector<Action> actions); // 查找最短路径
int main() {
string input_line; // 理解本题输入的是字符串
getline(cin, input_line);
vector<Action> actions;
string delimiter = " ";
//string sub_action = strtok(input_line, delimiter);
size_t pos = 0;
while ((pos = input_line.find(delimiter)) != string::npos) { // string::npos the end of the string
string action_str = input_line.substr(0, pos); // 截取从0开始,pos个字符。虽然字符串从0开始,但是pos的位置是空格的位置,则正好等于要截取的子串长度
input_line = input_line.substr(pos + 1); // 或者使用 input_line.erase(0, pos+delimiter.length();擦除第一个空格
Action action;
//int diret, steps;
//diret = stoi(action_str[0]);
// 截取方向和步数
size_t hyphen = action_str.find("-"); // 寻找连字符-
action.direction = stoi(action_str.substr(0, hyphen));
action.steps = stoi(action_str.substr(hyphen + 1)); // 截断从hyphen+1开始,到末尾的所有字符串
actions.push_back(action);
}
//print_vec(actions);
vector<Action> home_path = find_shortest_path(actions);
print_vec(home_path);
}
void print_vec(vector<Action> vec) {
for (vector<Action>::iterator it = vec.begin(); it != vec.end(); it++) {
cout << it->direction << "-" << it->steps;
if (it != vec.end()) cout << " ";
else cout << endl;
}
}
vector<Action> find_shortest_path(vector<Action> actions) {
stack<Action> path;
/*for (vector<Action>::iterator it = actions.begin(); it != actions.end(); it++) {
Action act = actions.top();
if(path.empty)
}*/
for (int i = 0; i < actions.size(); i++) {
Action act = actions[i]; // 从头开始,但压入栈底
if (path.empty()) path.push(act);
else {
Action top = path.top();
// 当前动作与栈顶动作相反,需进行抵消
if (abs(act.direction - top.direction) == 1) {
int new_steps = top.steps - act.steps;
if (new_steps == 0) // 刚好抵消
path.pop();
else if (new_steps > 0) { // 栈顶动作有步数剩余
// 更新栈顶步数
top.steps = new_steps;
path.pop();
path.push(top);
}
else { // 栈顶全抵消,往反方向走
act.steps = abs(new_steps);
path.pop();
path.push(act);
}
}
else { // 当前动作与栈顶动作方向不相反,直接压栈
path.push(act);
}
}
}
// 存储结果
vector<Action> final_path;
while (!path.empty()) {
Action act = path.top();
path.pop();
// 逆向
switch (act.direction) {
case 1: act.direction = 2; break;
case 2: act.direction = 1; break;
case 3: act.direction = 4; break;
case 4: act.direction = 3; break;
}
// 路径结果为空,直接存入
if (final_path.empty()) {
final_path.push_back(act);
continue;
}
// 获取路径结果中的最后一步
Action fin = final_path[final_path.size() - 1];
// 判断与当前行动是否同方向移动
if (act.direction == fin.direction) { // 是则合并
fin.steps += act.steps;
final_path[final_path.size() - 1] = fin;
}
else { // 否则直接存入结果路径中
final_path.push_back(act);
}
}
return final_path;
}
五、学习总结
1. 字符串接收
在<string>库中有函数getline,虽然与cin.getline()有区别,但是可以接收包含空格在内的一条字符串。用法:getline(cin,string); 输入的结果将保存在string对象中。
2. 字符串处理
根据题目需求,要将字符串按空格分割,c语言中有函数strtok可以使用,但是由于前面使用了string类型接收字符串,strtok便不适用了。考虑使用string类的函数进行分割。
思路:找到空格所在下标,截取从开头到空格的字符串
string.find("目标字符"),返回第一个匹配的字符所在下标。比如本题中:
string delimiter = " "; // 分隔符,寻找的目标字符
size_t pos = 0;
pos = input_line.find(delimiter); // 返回pos,字符串中第一个空格的下标
-----------------------------------------------------------------------------------------------
截取字串,通过substr实现。
substr(开始截取的下标) // 返回从下标开始,到结尾的子串
substr(开始截取的下标,截取的字符个数n) // 返回从下标开始,长度为n的子串
比如本题中:
string action_str = input_line.substr(0, pos); // 截取从0开始,pos个字符。虽然字符串从0开始,但是pos的位置是空格的位置,则正好等于要截取的子串长度
input_line = input_line.substr(pos + 1); // 或者使用 input_line.erase(0, pos+delimiter.length();擦除第一个空格
-----------------------------------------------------------------------------------------------
字符转整型,string to int,stoi函数。
action.steps = stoi(action_str.substr(hyphen + 1))
3. 栈和向量的使用
压栈用push,弹栈用pop,栈顶获取用top。
向量vector,插入用push_back,可以通过下标”随机访问“,获取大小size。