原题链接http://acm.scu.edu.cn/soj/problem.action?id=1091

这题的主要解法就是搜索,我用的是bfs,用map将二维数组处理成字符串作为主键,到达当前状态的最少步数作为键值,就能实现判重,如果当前最少步数已经超过10步,直接退出。但是这样做的时间是1292ms,虽然能够通过,但没能达到最优。

如果能够找到完美的编码函数,就可以不用map,时间应该能够更快,但是25!/12!/12!已经超过了数组的最大容量,找到编码函数也没用了。后来我又想到可以利用剪枝,因为这题限制了10步,设当前的位置不对的棋子数为h(x),当前已经走了y步,如果h(x)+y>10就说明不可能在10步之内完成,直接剪掉。

前面说过,可以把二维数组处理成为字符串作为主键,也可以把二维数组进行二进制压缩,用int作为主键应该会更快。

总之,最大的优化就是剪枝,剪枝后时间为0ms。这题用dfs+剪枝也能过,但没能判重,浪费了不少时间。比赛时,写A*代码应该最为简单。

AC代码:

#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=30;
int goal[][5]={
	1,1,1,1,1,
	0,1,1,1,1,
	0,0,2,1,1,
	0,0,0,0,1,
	0,0,0,0,0
};
const int dx[]={1,1,2,2,-1,-1,-2,-2};
const int dy[]={2,-2,1,-1,2,-2,1,-1};
int v[maxn];
struct node{
	int x,y;
	node(){
	}
	node(int x,int y):x(x),y(y){
	}
};

void deal(){
	v[0]=1;
	for(int i=1;i<27;++i) v[i]=2*v[i-1];
}
inline int get1(int (*a)[5]){  //统计位置不对的棋子
	int c=0;
	for(int i=0;i<5;++i)
	for(int j=0;j<5;++j){
		if(a[i][j]==2) continue;
		else {
			if(a[i][j]!=goal[i][j]) ++c;
		}
	}
	return c;
}
int get2(int (*a)[5]){  //二进制的值
	int c=0;
	for(int i=0;i<5;++i)
	for(int j=0;j<5;++j){
		c+=a[i][j]*v[i*5+j];
	}
	return c;
}
 int get3(int (*a)[5]){ //空地的位置
	for(int i=0;i<5;++i)
	for(int j=0;j<5;++j)
		if(a[i][j]==2) return i*5+j;
}
void get4(int (*a)[5],int h,int pos){ //解码
	h-=2*v[pos];
	a[pos/5][pos%5]=2;
	for(int i=0;i<25;++i){
		if(i==pos) {
			h=h>>1;
			continue;
		}
		else {
			a[i/5][i%5]=h&1;
			h=h>>1;
		}
	}
}
int bfs(int (*a)[5]){
	if(get1(a)>10) return -1;
	map<int,node>ha;
	queue<int>q;
	int f=get2(a);
	q.push(f);
	ha[f]=node(0,get3(a));
	while(!q.empty()){
		int h=q.front();
		q.pop();
		int b[5][5];
		node g=ha[h];
		get4(b,h,g.y);
		int d1=get1(b);
		if(d1+g.x>10) continue;
		if(d1==0) return g.x;
		int x=g.y/5,y=g.y%5;
		int old[5][5];
		for(int i=0;i<8;++i){
			int nx=x+dx[i],ny=y+dy[i];
			if(nx<0||ny<0||nx>=5||ny>=5) continue;
			memcpy(old,b,sizeof(b));
			swap(old[x][y],old[nx][ny]);
			int k=get2(old);
			if(ha.count(k)) continue;
			q.push(k);
			ha[k]=node(ha[h].x+1,get3(old));
		}
	}
	return -1;
}
int main(){
	deal();
	int T;
	scanf("%d",&T);
	char ch[5][5];
	int s[5][5];
	while(T--){
		for(int i=0;i<5;++i)
			scanf("%s",ch[i]);
		for(int i=0;i<5;++i)
		for(int j=0;j<5;++j){
			s[i][j]=ch[i][j]-'0';
		}
		int ans=bfs(s);
		if(ans==-1) printf("Unsolvable in less than 11 move(s).\n");
		else printf("Solvable in %d move(s).\n",ans);
	}
	return 0;
}

如有不当之处欢迎指出!

soj1091 指环王 bfs+hash+剪枝的更多相关文章

  1. NOIP 模拟 玩积木 - 迭代加深搜索 / bfs+hash+玄学剪枝

    题目大意: 有一堆积木,0号节点每次可以和其上方,下方,左上,右下的其中一个交换,问至少需要多少次达到目标状态,若步数超过20,输出too difficult 目标状态: 0 1 1 2 2 2 3 ...

  2. 【BZOJ】1054: [HAOI2008]移动玩具(bfs+hash)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1054 一开始我还以为要双向广搜....但是很水的数据,不需要了. 直接bfs+hash判重即可. # ...

  3. [BZOJ1054][HAOI2008]移动玩具 bfs+hash

    1054: [HAOI2008]移动玩具 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2432  Solved: 1355[Submit][Stat ...

  4. HDU-1043 Eight八数码 搜索问题(bfs+hash 打表 IDA* 等)

    题目链接 https://vjudge.net/problem/HDU-1043 经典的八数码问题,学过算法的老哥都会拿它练搜索 题意: 给出每行一组的数据,每组数据代表3*3的八数码表,要求程序复原 ...

  5. hdu.1067.Gap(bfs+hash)

    Gap Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  6. BFS+Hash(储存,判重) HDOJ 1067 Gap

    题目传送门 题意:一个图按照变成指定的图,问最少操作步数 分析:状态转移简单,主要是在图的存储以及判重问题,原来队列里装二维数组内存也可以,判重用神奇的hash技术 #include <bits ...

  7. POJ3697+BFS+hash存边

    /* 疾速优化+hash存边 题意:给定一个包含N(1 ≤ N ≤ 10,000)个顶点的无向完全图,图中的顶点从1到N依次标号.从这个图中去掉M(0 ≤ M ≤ 1,000,000)条边,求最后与顶 ...

  8. poj 2046 Gap(bfs+hash)

    Description Let's play a card game called Gap. You have cards labeled with two-digit numbers. The fi ...

  9. poj 2697 A Board Game(bfs+hash)

    Description Dao was a simple two-player board game designed by Jeff Pickering and Ben van Buskirk at ...

随机推荐

  1. python_如何进行反向迭代和实现反向迭代?

    案例: 实现一个连续的浮点数发生器,FloatRange,根据给定范围(start, end) 和步进值,产生一些列的浮点数,例如:FloatRange(3,4,0.2),将产生下列序列: 正向:3. ...

  2. TCP/IP详解 卷1 第二十一章 TCP的超时与重传

    21.1 引言 可靠性的保证之一就是超时重传 前面两个超时重传的例子 1)  ICMP端口不能到达时,TFTP客户使用UDP实现了一个简单的超时和重传机制,假定5s是一个适当是时间间隔,并每隔5s进行 ...

  3. win10下端口被占用解决办法

    调试socket通信的时候,如果程序未正常结束,重新启动会报端口被占用的错误. 一 查找被占用的端口 解决办法如下:按win+R输入cmd打开控制台,输入 netstat -ano|findstr 8 ...

  4. Intellij IDEA 15 如何同时打开多个项目

    标题:Intellij IDEA 15 如何同时打开多个项目 作者原创技术文章,转载请注明出处 我们在编程时常常需要打开多个项目,例如操作复制黏贴或者参考其他项目等等,但是编译器Intellij ID ...

  5. Linux CentOS 7 下 JDK 1.7 安装与配置

    前言 简单记录一下在CentOS 7中安装配置JDK 1.7的全过程~ 下载 首先是jdk 1.7 64bit & 32bit的下载地址: jdk-7u79-linux-x64.tar.gz  ...

  6. LANMP系列教程之php编译安装CentOS7环境

    前提:必须先安装好MySQL以及Apache   1.准备好源码包并配置好yum源,需要的源码包包括: libmcrypt-2.5.8-9.el6.x86_64.rpm libmcrypt-devel ...

  7. Java锁概念基础

    Java中的锁不管是Lock还是synchronized都可以分为互斥锁和非互斥锁. 互斥锁只能被一个线程持有,其他线程只能等待锁的释放.synchronized,ReentrantLock,Read ...

  8. 洛谷 [P1801] 黑匣子

    这道题是一道splay裸题,然而身为蒟蒻的我并不会,所以这道题我维护的是一个大根堆与一个小根堆结合起来的类似沙漏的结构. 本题难点在于询问的不是最大最小值,而是第K小值,所以我们想到了维护这样两个堆, ...

  9. COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

    传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的 ...

  10. HTML标签的命名/CSS标准化命名大全

    在一个内容较多的HTML页面中,需要设计许多不同的框架,再为这些不同的框架及内容进行分类,给予相应的名称,从而使得网页结构更加清晰,也为工作提供了方便.许多新手朋友在设计一个HTML文件时,可能只会依 ...