20-1-20-图的深度优先和广度优先遍历-POJ1184

POJ1184

聪明的打字员

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 8480Accepted: 1842

Description

阿兰是某机密部门的打字员,她现在接到一个任务:需要在一天之内输入几百个长度固定为6的密码。当然,她希望输入的过程中敲击键盘的总次数越少越好。
不幸的是,出于保密的需要,该部门用于输入密码的键盘是特殊设计的,键盘上没有数字键,而只有以下六个键:Swap0, Swap1, Up, Down, Left, Right,为了说明这6个键的作用,我们先定义录入区的6个位置的编号,从左至右依次为1,2,3,4,5,6。下面列出每个键的作用:
Swap0:按Swap0,光标位置不变,将光标所在位置的数字与录入区的1号位置的数字(左起第一个数字)交换。如果光标已经处在录入区的1号位置,则按Swap0键之后,录入区的数字不变;
Swap1:按Swap1,光标位置不变,将光标所在位置的数字与录入区的6号位置的数字(左起第六个数字)交换。如果光标已经处在录入区的6号位置,则按Swap1键之后,录入区的数字不变;
Up:按Up,光标位置不变,将光标所在位置的数字加1(除非该数字是9)。例如,如果光标所在位置的数字为2,按Up之后,该处的数字变为3;如果该处数字为9,则按Up之后,数字不变,光标位置也不变;
Down:按Down,光标位置不变,将光标所在位置的数字减1(除非该数字是0),如果该处数字为0,则按Down之后,数字不变,光标位置也不变;
Left:按Left,光标左移一个位置,如果光标已经在录入区的1号位置(左起第一个位置)上,则光标不动;
Right:按Right,光标右移一个位置,如果光标已经在录入区的6号位置(左起第六个位置)上,则光标不动。
当然,为了使这样的键盘发挥作用,每次录入密码之前,录入区总会随机出现一个长度为6的初始密码,而且光标固定出现在1号位置上。当巧妙地使用上述六个特殊键之后,可以得到目标密码,这时光标允许停在任何一个位置。
现在,阿兰需要你的帮助,编写一个程序,求出录入一个密码需要的最少的击键次数。

Input

仅一行,含有两个长度为6的数,前者为初始密码,后者为目标密码,两个密码之间用一个空格隔开。

Output

仅一行,含有一个正整数,为最少需要的击键次数。

Sample Input

123456 654321

Sample Output

11

Source

Noi 01

  • 自己写的代码挂了, 参考别人的代码剪枝剪枝
  • 具体的剪枝方式是剪枝了位置[1,4](从0开始)的左移和右移操作, 这四个位置的数字的改变;
    • 当光标位置的数字和目标状态相同时, 需要通过光标的左移和右移去改变其他位置的数字
    • 换句话说, 当光标位置的数字和目标状态不同时, 需要通过其他的操作改变该位置的数字, 因为迟早要改变, 不如现在就变.
    • 详细的解释在代码中
  • 有必要多去了解一下双端bfs, 状圧和剪枝的一些方式.
#pragma warning(disable:4996)
#include<cstdio>
#include <iostream>
#include <map>
#include <string> 
#include <queue> 
using namespace std;

struct Node {
	string s;//6位数字, 第七位存储当前光标所在的位置[0,5]
	int step;//达到的最小的步数
	Node(string s_ = "", int step_ = 0) :s(s_), step(step_) {}
	~Node() {}
};

//起点和终点
Node beg, en;
//一个hash,判断该状态是否出现过
map<string, int> mp;
queue<Node> q;
int bfs() {
	Node tt;
	int i;

	while (!q.empty()) {
		q.pop();
	}
	q.push(beg);
	mp[beg.s] = 1;
	while (!q.empty()) {
		Node top = q.front();
		q.pop();
		string ss;
		ss = top.s;
		for (i = 0; i < 6; i++) {
			if (ss[i] != en.s[i]) {
				break;
			}
		}
		if (i == 6) {
			return top.step;
		}

		//Swap0:
		ss = top.s;
		swap(ss[0], ss[ss[6] - '0']);
		if (!mp.count(ss)) {
			tt.s = ss;
			tt.step = top.step + 1;
			q.push(tt);
			mp[ss] = 1;
		}

		//Swap1:
		ss = top.s;
		swap(ss[5], ss[ss[6] - '0']);
		if (!mp.count(ss)) {
			tt.s = ss;
			tt.step = top.step + 1;
			q.push(tt); mp[ss] = 1;
		}

		//Up:
		ss = top.s;
		if (ss[ss[6] - '0'] != '9' && ss[ss[6] - '0'] != en.s[ss[6] - '0']) {
			ss[ss[6] - '0'] += 1;
		}
		if (!mp.count(ss)) {
			tt.s = ss;
			tt.step = top.step + 1;
			q.push(tt);
			mp[ss] = 1;
		}

		//Down
		ss = top.s;
		if (ss[ss[6] - '0'] != '0' && ss[ss[6] - '0'] != en.s[ss[6] - '0']) {
			ss[ss[6] - '0'] -= 1;
		}
		if (!mp.count(ss)) {
			tt.s = ss; tt.step = top.step + 1;
			q.push(tt); mp[ss] = 1;
		}

		//shift left
		//左移的操作[1,4]的位置区间里面:
			//如果[1,4]的区间里面光标所在的位置和目标状态不同, 进行左移和右移操作没有意义
			//这里的证明?
			//所有仅仅当相同的时候移动光标
		ss = top.s;
		if (ss[6] != '0') {
			if (ss[6] != '5') {//光标所在的位置为1,2,3,,当且仅当相同的时候移动光标
				if (ss[ss[6] - '0'] == en.s[ss[6] - '0']) {
					ss[6] -= 1;
				}
			}
			else {//光标所在的位置为5
				ss[6] -= 1;
			}
		}
		if (!mp.count(ss)) {
			tt.s = ss; tt.step = top.step + 1;
			q.push(tt); mp[ss] = 1;
		}

		//shift right
		ss = top.s;
		if (ss[6] - '0' != 5) {
			if (ss[6] != '0') {
				if (ss[ss[6] - '0'] == en.s[ss[6] - '0']) ss[6] += 1;
			}
			else {
				ss[6] += 1;
			}
		}
		if (!mp.count(ss)) {
			tt.s = ss;
			tt.step = top.step + 1;
			q.push(tt);
			mp[ss] = 1;
		}
	}
}
int main() {
	while (cin >> beg.s >> en.s) {
		mp.clear();
		beg.s += '0';
		beg.step = 0;
		cout << bfs() << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值