链接:登录—专业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<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<algorithm>
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];//从什么方向来的
map<string,int>mp;
string a[N];//a[i]表示第i个状态的字符串
int cnt=0;//mp中有多少个元素
queue<int>q;
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<<cur<<endl;
q.pop();
int pos=cur.find('x');//获取x的位置
int x=pos/3,y=pos%3;
for(int i=0;i<4;++i){
int dx=x+dir[i][0];
int dy=y+dir[i][1];
if(dx<0||dy<0||dx>=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<<from[x];
}
}
int main(){
for(int i=1;i<=9;++i){
char a;
cin>>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<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<map>
#include<vector>
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];
vector<string>st;
map<string,int>mp;
queue<int>q;
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<<cur<<endl;
int l=cur.length();
for(int i=1;i<n;++i){
for(int j=0;j<l;){
string nxt=cur;
int pos=cur.find(a[i],j);
if(pos==string::npos) break;
j=pos+1;
nxt.erase(pos,a[i].length());
nxt.insert(pos,b[i]);
if(mp.find(nxt)==mp.end()){
mp[nxt]=++cnt;
st.push_back(nxt);
dis[cnt]=dis[tmp]+1;
q.push(cnt);
if(nxt==B) return 1;//<=对第二次广搜没用
}else if(dis[mp[nxt]]==-1){//在第二次广搜中出现,意味着a走过了,且是走到末路的情况
dis[mp[nxt]]=dis[tmp]+1;//这时b在之前的基础上加步数,实际上这相当于已经有一组解了
q.push(cnt);//实际上,disa[tmp]=disa[mp[nxt]],即到达这个字符串的步数
}//这里push的原因是,cnt可以帮助我们接下来查找下一个字符串
}
}
}
return 0;
}
int main(){
cin>>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<<disa[mp[B]];
return 0;
}
bfs(B,disb,b,a);
int ans=1000;
for(int i=0;i<=cnt;++i){
if(disa[i]!=-1&&disb[i]!=-1){
ans=min(ans,disa[i]+disb[i]);
}
}
if(ans>10) cout<<"NO ANSWER!";
else cout<<ans;
return 0;
}
又是被自己菜枯的一天QAQ