[NOIP2002]字串变换题解

题目链接:[NOIP2002]字串变换 (nowcoder.com)

题意:给你一个初始字符串和一个目标字符串,还有一些变换规则,问你用这些变换规则最小要多少步可以将初始字符串变为目标字符串。

看体面感觉没什么,而且数据范围也不大,我直接写了一个bfs,交了,洛谷上过了(P1032 [NOIP2002 提高组] 字串变换 - 洛谷 ),但是牛客没过,t了,这就很离谱了,我应该说牛客数据很顶还是说洛谷数据太水了呢,而且洛谷上这题还是还是绿题(?

思路:既然单向搜索t了,那就改成双向搜索,题目中说十步之内,所以我们正向搜5步,反向搜5步,如果反向搜索的时候遇到了正向搜索的时候搜过的,就更新最小值,但是我的这个代码复用性太差了,改成一个可能会好很多,不过清晰也是真的,便于理解嘛,写的时候写完一个改一改另一个就写好了.

还有一个小地方我第一次写的时候没注意:我们用一个规则去搜索的时候,它不一定只对应一个位置,所以我们一定要把一个状态下,某一个变换规则的所有可能位置全部搜索一遍,不然就会缺状态

#include <bits/stdc++.h>
using namespace std;
string from[10];
string to[10];
int num=0;
map<string,int>tes1;
map<string,int>tes2;
int flag=0;
int ans=100;
struct node{
	string x;
	int step;
	node(string a,int t){
		this->x=a;
		this->step=t;
	}
}; 

void bfs1(string beg){
	queue<node>q;
	q.push(node(beg,0));
	tes1[beg]=0;
	while(!q.empty()){
		node t=q.front();
		//cout<<t.x<<" "<<t.step<<endl;
		if(t.step>=5){
			return;
		}	
		q.pop();		
		for(int i=0;i<num;i++){
			string x=t.x;
			string tem=x;
			int pos=x.find(from[i]);
			while(pos!=std::string::npos){
				tem=t.x;
				x.replace(pos,from[i].size(),string(from[i].size(),'.'));
				tem.replace(pos,from[i].size(),to[i]);
				if(tes1.count(tem)!=0){
					pos=x.find(from[i]);
					continue;
				}
				q.push(node(tem,t.step+1));
				tes1[tem]=t.step+1;
				pos=x.find(from[i]);
			}
		}
	} 
}

void bfs2(string beg){
	queue<node>q;
	q.push(node(beg,0));
	tes2[beg]=0;
	while(!q.empty()){
		node t=q.front();
		//cout<<t.x<<" "<<t.step<<endl;
		if(t.step>=5){
			return;
		}	
		q.pop();		
		for(int i=0;i<num;i++){
			string x=t.x;
			string tem=x;
			int pos=x.find(to[i]);
			while(pos!=std::string::npos){
				tem=t.x;
				x.replace(pos,to[i].size(),string(to[i].size(),'.'));
				tem.replace(pos,to[i].size(),from[i]);
				if(tes2.count(tem)!=0){
					pos=x.find(to[i]);
					continue;
				}
				if(tes1.count(tem)!=0){
					ans=min(tes1[tem]+t.step+1,ans);
				}
				q.push(node(tem,t.step+1));
				tes2[tem]=t.step+1;
				pos=x.find(to[i]);
			}
		}
	} 
}

int main(){
	std::ios::sync_with_stdio(false);
	string beg,aim;
	cin>>beg>>aim;
	while(cin>>from[num]>>to[num]){
		num++;
	}
	//双向搜索,先用beg搜5层
	bfs1(beg);
//	for(auto i:tes1){
//		cout<<i.first<<" "<<i.second<<endl;
//	}	
	//再用aim搜五层,如果搜aim的时候遇到了用beg搜的时候搜过的状态就记录 
	bfs2(aim);
//	for(auto i:tes2){
//		cout<<i.first<<" "<<i.second<<endl;
//	}
	if(ans==100){
		cout<<"NO ANSWER!"<<endl;
	}else{
		cout<<ans<<endl;
	}
	return 0;
}

原先的单项搜索的复杂度为O(2^n)这种双向搜索的方法可以将搜索的复杂度降低到O(2^(n/2)),我也不知道我推的对不对,当n比较大的时候复杂度的提升是很明显的,算是一种剪枝策略。其实就是原先的状态树有n层,现在变成了两个有n/2层的状态树。

Guess you like

Origin blog.csdn.net/m0_58178876/article/details/121260447