搜索与搜索剪枝3

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

八数码


You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'.

You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.

此题堪称深搜广搜的完美结合,外加数据处理能力的考察,不可多得,值得多做(还不是因为你想不出来)!QAQ

核心思想:可以通过广搜进行下一步状态的确定,并且要有对重复的判断以免多走,同时由于广搜的性质决定了一定是最小步数;直到找到预期的答案停止查找,后通过之前记录的信息深搜递归一步步返回进行答案的输出。我们将数字串直接转化为字符串(若当成九位数字会是longlong类型,比较麻烦),并用map映射成一个数字,用以标记是否记录过某字符串,并提供找到答案时的标号用以回溯;q记录访问编号,问为啥不直接记录探索过的string?因为需要广搜起点的标号用以回溯,所以就用q存标号,这样只要再加一个数字映射为字符串的数组就行了;a的string类型字符串就是用做这个。

细节问题:回溯时的初始标号当然是找到答案时的数字了;好好想想操作到底对应着哪个方向QAQ

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1030,inf=0x3f3f3f3f,N=363000;//状态总数:9的全排列 
string s="",t="12345678x";
int last[N];//用于回溯原状态
char from[N];//从什么方向来的 
mapmp;
string a[N];//a[i]表示第i个状态的字符串 
int cnt=0;//mp中有多少个元素 
queueq;
const int dir[4][2]={0,1,0,-1,-1,0,1,0};
const char d[4]={'r','l','u','d'};
bool bfs(string s,string t){
	mp[s]=1;//第一个元素
	cnt=1;
	a[1]=s;//作用:开一个双向的映射,把字符串映射成数字,反之也可 
	q.push(1); 
	while(!q.empty()){
		int tmp=q.front();
		string cur=a[tmp];//将当前字符串取出,找x的位置
//		cout<=3||dy>=3) continue;//越界 
			string nxt=cur;//不能改动原来的字符串 
			swap(nxt[pos],nxt[dx*3+dy]);//将x的位置交换
			if(mp.find(nxt)!=mp.end()) continue;//若发现以前走过,继续搜 
			mp[nxt]=++cnt;//下一个状态 
			a[cnt]=nxt;//存本状态的字符串 
			last[cnt]=tmp;//上一个元素的位置 
			from[cnt]=d[i];// 从哪个方向来
			q.push(cnt);
			if(nxt==t) return 1; 
		} 
	} 
	return 0;
}
void dfs(int x){
	if(last[x]!=0){
		dfs(last[x]);
		cout<>a;
		s+=a;
	}
	if(bfs(s,t)==0) cout<<"unsolvable";
	else dfs(mp[t]);
	return 0;
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

[NOIP2002]字串变换

已知有两个字串 A, B及一组字串变换的规则(至多6个规则):
A1 -> B1
A2 -> B2
规则的含义为:在A中的子串 A1可以变换为 B1、A2可以变换为 B2 …。
例如:A='abcd' B='xyz'
变换规则为:
‘abc’->‘xu’ ‘ud’->‘y’ ‘y’->‘yz’
则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
‘abcd’->‘xud’->‘xy’->‘xyz’
共进行了三次变换,使得A变换为B。

此题最主要的卡壳点在于,如果让原字符串与答案字符串双向奔赴,那么就可以将问题规模减少一半,从而减少不必要的运行时间与占用内存;

细节问题:1、不要死板回溯ans,可以开一个dis数组存放步数;2、要是数组空间给得比较紧,可以通过使用vector,用多少拿多少;3、while(cin>>a[n]>>b[n]) n++ 读到返回1,否则返回0 ;4、scanf("%d%d%d") 读够3个数返回3,没读够返回EOF 。

string用法整理:

判断是否字符串中没有指定子串:str.find(substr)==string::npos

指定位置之后子串位置:str.find(substr, pos)

替换字符串中指定子串:str.replace(pos, len, targetstr)

擦除指定部位子串:str.erase(pos, substr.length)

指定部位插入子串,原位置字符后移:str.insert(pos, substr)

memset使用细节:memset(array,0,sizeof(array)) 清空array时,若array是作为指针传入则会无法获取sizeof(array),所以不能在函数中对传入的指针直接使用memset。

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1000000,inf=0x3f3f3f3f;
string A,B,a[8],b[8];
int disa[N],disb[N];
vectorst;
mapmp;
queueq;
int n=1,cnt=-1;
bool bfs(string s,int dis[],string a[],string b[]){
	while(!q.empty()) q.pop();
	mp[s]=++cnt;//与st一致,从0开始算下标 
	st.push_back(s); 
	dis[cnt]=0;
	q.push(cnt);
	while(!q.empty()){
		int tmp=q.front(); q.pop();
		if(dis[tmp]==5) return 0;
		//由于是对半搜,所以不能超过5(另一边就交给下一次吧,如果真能合并 就一定能 
		string cur=st[tmp];
//		cout<>A>>B;
	while(cin>>a[n]>>b[n]) n++;
	memset(disa,-1,sizeof(disa));
	memset(disb,-1,sizeof(disb));
	if(bfs(A,disa,a,b)) {
		cout<10) cout<<"NO ANSWER!"; 
	else cout<

又是被自己菜枯的一天QAQ 

你可能感兴趣的:(算法学习,剪枝,算法)