算法1
(手写hash)
注:蓝桥杯可使用map(有序)函数,但此题测试用例会超时
#include<bits/stdc++.h>
using namespace std;
//该结点是将二维数组状态压缩成一个字符串
typedef unsigned long long ULL;
const int M=10000000;
const int N=100010,P=13331;
ULL h[N];//h[]存放字符串的前缀值
int d[M];
ULL hash_CSM(string a)
{
h[0]=0;
for(int i=1;i<=9;i++)
{
h[i]=h[i-1]*P+(a[i-1]-'0'+1);
}
return h[9]%M;
//y总讲hash时用ULL实现自动取余,但是此题hash值需要作为数组下标,对该结点进行标记,所以通过多次试验,找到M可实现不冲突,且数组可开辟这些空间
}
int bfs(string state)
{
queue<string>q;//队列存放节点
//unordered_map<string,int>d;//存放到达该结点的最短步数(d[string]的值)
q.push(state); //节点入队
//cout<<hash(state);
d[hash_CSM(state)]=0;//到达该结点的步数为零步
string end="12345678x";
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};//遍历当前状态下该结点上下左右四个方向
while(q.size())//队列中有节点
{
string t=q.front();//临时变量t等于队列中的头结点
q.pop();//出队
if(t==end)return d[hash_CSM(t)];//找到答案,返回最短路径
int distance=d[hash_CSM(t)];//临时变量distance存放到达当前节点的最短路径
/*为什么需要distance临时变量?
因为下面改变字符串后,字符串t更新,找不到该状态下的上一个状态*/
int k=t.find('x');//找到可变换节点的坐标
int x=k/3,y=k%3;//转换为二维,为了寻找其上下左右四个节点
for(int i=0;i<4;i++)
{
int a=x+dx[i];
int b=y+dy[i];
if(a>=0&&a<3&&b>=0&&b<3)
{
swap(t[a*3+b],t[k]);
//在一维中将符合条件的节点和可变换节点进行位置交换
//cout<<t<<endl;
//cout<<"字符串的哈希值"<<hash(t)<<endl;
//cout<<"到该节点的步数"<<d[hash(t)]<<endl;
if(!d[hash_CSM(t)])//d数组初始化时0,是0代表该结点 未被访问过
{
d[hash_CSM(t)]=distance+1;
q.push(t);
}
swap(t[a*3+b],t[k]);//字符串恢复到原来,继续遍历下种状态是否可行
}
}
}
return -1;
}
int main()
{
string state;
char c;
for(int i=0;i<9;i++)
{
cin>>c;
state+=c;
}
cout << bfs(state) << endl;
return 0;
}
算法2
(用unordered_map函数)
#include<bits/stdc++.h>
using namespace std;
//该结点是将二维数组状态压缩成一个字符串
int bfs(string state)
{
queue<string>q;//队列存放节点
unordered_map<string,int>d;
//存放到达该结点的最短步数(d[string]的值)
q.push(state); //节点入队
d[state]=0;//到达该结点的步数为零步
string end="12345678x";
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
//遍历当前状态下该结点上下左右四个方向
while(q.size())//队列中有节点
{
string t=q.front();//临时变量t等于队列中的头结点
q.pop();//出队
if(t==end)return d[t];
//找到答案,返回最短路径
int distance=d[t];
//临时变量distance存放到达当前节点的最短路径
/*为什么需要distance临时变量?
因为下面改变字符串后,字符串t更新,找不到该状态下的上一个状态*/
int k=t.find('x');
//找到可变换节点的坐标
int x=k/3,y=k%3;
//转换为二维,为了寻找其上下左右四个节点
for(int i=0;i<4;i++)
{
int a=x+dx[i];
int b=y+dy[i];
if(a>=0&&a<3&&b>=0&&b<3)
{
swap(t[a*3+b],t[k]);
//在一维中将符合条件的节点和可变换节点进行位置交换
if(!d.count(t))
{
d[t]=distance+1;
q.push(t);
}
swap(t[a*3+b],t[k]);
//字符串恢复到原来,继续遍历下种状态是否可行
}
}
}
return -1;
}
int main()
{
string state;
char c;
for(int i=0;i<9;i++)
{
cin>>c;
state+=c;
}
cout << bfs(state) << endl;
return 0;
}
谢谢!
今年不知道能用不,如果更新到c++11,直接就能用
好的,据说蓝桥杯支持C++11
可以用c++11
是的!谢谢
那是不是auto可以用了?
为什么非要找x的位置呢?
int k=t.find('x');
其他字符1–8咋不行?是因为题目限制了只让
x
交换?如果不限制呢,任何元素都可交换,求到最终状态的最少步数大佬好厉害
是右左上下吧
写的真好,不过这里的N是能接收字符串的最大长度吧,其实开到10就够了
这哈希值取模不会冲突的吗,咋这么玄乎🤔
M = 10000007,p就能为131了,玄学hash😅
蓝桥杯不是可以用stl吗
蓝桥杯不支持c++11
好像可以啊,unordered_map,priority_queue什么的好像都可以
emm请问哪写的这些啊,我问我同学,他说可以用unordered_map,priority_queue这些
emm因为蓝桥官方指定用devcpp,我比赛的时候是用不了unordered_map/set的,还有一些函数也用不了,除非用std=c++11来打开。当然也许蓝桥的评测系统支持,但是我不敢试hhh
现在好像也可以Codeblocks20:03
那就好,dev太辣鸡了
嗯,官网上有说两个都可以用
M改成8000000过了
厉害了👍👍👍
内存超了我的天
这怎么看出来 P=131 存在哈希冲突? P=13331不存在 ? P=131 WA P=13331 AC了
不能AC应该就是存在哈希冲突吧😅
交了一发 不是才看出来嘛。。 这种题 就试试 如果P=131 wa了 就换成13331?
但是 有1说1 你写的挺好 👍
嗯嗯,是的,需要反复实验才能AC
hhh,谢谢😄
i了i了,c语言只能哈希了吧
hhh,y总是这么讲的😅
请问一下ULL hash_CSM(string a)函数里面,为什么for循环要到10,字串长度不是为9吗
还有就是为什么用h[10]%N,而不是用h[9] % N
已改p值为13331,for循环到9,也不存在哈希冲突,可以AC。
for循环到9时存在哈希冲突,不能AC。所以改成循环到10,将整体值乘以131,为了降低哈希冲突。