NKOJ 3631 密码锁

NKOJ 3631 密码锁

思路

  • BFS经典题。

实现方法

  • 用一个结构体存储当前密码锁的状态和已经走过的步数。
  • 将开始的状态入队。
  • 每次取出队首,枚举所有可能情况。
    • 每一位的上下拨动。
    • 每两位之间的交换。
    • \(11\) 种情况。
  • 给入队的情况打标记。

代码

#include<map>
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{int n1,n2,n3,n4,stp;};
int work(node x){
	return x.n1*1000+x.n2*100+x.n3*10+x.n4; 
}
int mp[9999];
int change(int x){
	if(x>=9) return 1;
	else return x+1; 
}
int shift(int x){
	if(x<=1) return 9;//注意点1
	else return x-1;
}
void bfs(node st,node en){
	queue<node> que;
	que.push(st);
	while(!que.empty()){
		node tp=que.front();
		que.pop();
		if(tp.n1==en.n1&&tp.n2==en.n2&&tp.n3==en.n3&&tp.n4==en.n4){
			printf("%d",tp.stp);
			return;
		}
		node nxt=tp;
		nxt.stp++;
		//1
		nxt.n1=change(tp.n1);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n1=shift(tp.n1);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n1=tp.n1;
		//2
		nxt.n2=change(tp.n2);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n2=shift(tp.n2);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n2=tp.n2;
		//3
		nxt.n3=change(tp.n3);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n3=shift(tp.n3);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n3=tp.n3;
		//4
		nxt.n4=change(tp.n4);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n4=shift(tp.n4);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		nxt.n4=tp.n4;
		//1,2
		swap(nxt.n1,nxt.n2);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		swap(nxt.n1,nxt.n2);
		//2,3
		swap(nxt.n2,nxt.n3);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		swap(nxt.n2,nxt.n3);
		//3,4
		swap(nxt.n3,nxt.n4);
		if(mp[work(nxt)]==0) que.push(nxt),mp[work(nxt)]=1;
		swap(nxt.n3,nxt.n4);
	}
}
int main(){
	node st,en;
	scanf("%d%d%d%d",&st.n1,&st.n2,&st.n3,&st.n4);
	scanf("%d%d%d%d",&en.n1,&en.n2,&en.n3,&en.n4);
	st.stp=0,en.stp=0;
	bfs(st,en);

	return 0;
}

本题特色

  • Q:为什么状压?
  • A:在打标记时原本状态较多,每一个状态如果用一个 int 存储将会光荣 MLE
  • Q:怎么压?
  • A:注意到题目说每一位都在 \(1 \sim 9\) 之间。这时用能存储 \(2 \times 10^9\) 大小的 int 有亿点浪费,而一个整数的每一位刚好能存 \(1 \sim 9\) 之间的数(在保证首位不为 \(0\) 的情况下)。所以将每个状态标记转化为十进制数存储,大大节省数组空间。

注意事项

  1. 注意在写变换函数的时候,不要把 \(+1\)\(-1\) 搞错了。
posted @ 2024-12-14 09:50  hsr_ray  阅读(13)  评论(0)    收藏  举报