机试刷题:老鼠回家路(字符串、栈、向量)

一、题目

老鼠找食物,但是回家的时候找到最短路输入是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。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值