这题我第一次用的bfs + ELFhash,直接TLE,又换成bfs + 康托还是TLE,5000ms都过不了!!我一直调试,还是TLE,我才发觉应该是方法的问题。

今天早上起床怒学了一波A*算法,因为IDA*我很熟悉,因此A*也学得很快,曼哈顿距离也很容易理解,看了好多人都用的A*过掉的。我一直在想A*算法无法保证得到最短路啊,怎么能AC?我擦,后来一读题目,题目没说要最短路径啊,只要任意一条路就可以了。我就呵呵了,愤怒写了一波A*过掉。

思路:

1. 根据逆序数可以判断是否有解,如果当前逆序数为奇数一定无解,是偶数则进行搜索。

2. 利用康拓判重。

3.  启发函数 : F = G + H. A*算法的精髓, G表示从初始状态到达当前状态的花费,H表示当前状态到达目标状态的花费,H只能大概估计,估计可用曼哈顿距离。曼哈顿距离介绍

AC代码

#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 4e5 + 5;
int vis[maxn]; //close表用于判重
int goal[] = {1,2,3,4,5,6,7,8,0};
int st[9];
typedef int state[9];

struct node{
	int a[9];
	int g, h; // 估价函数
	int ha, pos;
	node() {
	}
	node(int *b, int g, int h, int ha, int pos):g(g), h(h), ha(ha), pos(pos){
		memcpy(a, b, sizeof(a));
	}
	bool operator < (const node &p) const {
		return h > p.h || (p.h == h && g > p.g); // h小在前,g小在前
	}
};

struct Node{
	int pre;
	char dir;
}Pre[maxn];

const int dx[] = {0,0,-1,1};
const int dy[] = {1,-1,0,0};
const char dir[] = {'r','l','u','d'};

int fact[9];
void deal() {  //1~8阶乘打表,方便编码
	fact[0] = 1;
	for(int i = 1; i < 9; ++i) fact[i] = fact[i - 1] * i;
}

int cut(int *a) {
	int cnt = 0;
	for(int i = 0; i < 9; ++i) {
		if(a[i] == 0) continue;
		for(int j = i + 1; j < 9; ++j) {
			if(a[j] != 0 && a[i] < a[j]) ++cnt;
		}
	}
	return cnt;
} 

int KT(int *a) {
	int code = 0;
	for(int i = 0; i < 9; ++i) {
		int cnt = 0;
		for(int j = i + 1; j < 9; ++j) if(a[j] < a[i]) cnt++;
		code += fact[8 - i] * cnt;
	}
	return code;
} 

void print(int u) {
	if(Pre[u].pre == -1) return;
	print(Pre[u].pre);
	printf("%c", Pre[u].dir);
}

int get_h(int *a) { //根据曼哈顿距离得到的估价函数
	int cnt = 0;
	for(int i = 0; i < 9; ++i) {
		if(!a[i]) continue;
		int x = (a[i] - 1) / 3, y = (a[i] - 1) % 3; //终点的position
		cnt += abs(i / 3 - x) + abs(i % 3 - y);
	}
	return cnt;
}

int Astar() {
	priority_queue<node>q;
	int des = KT(goal); //目标
	memset(vis, 0, sizeof(vis));
	int code = KT(st);
	vis[code] = 1;
	Pre[code].dir = 'x';
	Pre[code].pre = -1;
	int pos;
	for(int i = 0; i < 9; ++i) if(!st[i]) pos = i;
	int g = 0, h = get_h(st);
	q.push(node(st, g, h, code, pos));
	while(!q.empty()) {
		node p = q.top();
		q.pop();
		code = p.ha, g = p.g, h = p.h, pos = p.pos;
		if(code == des) {  // 找到答案
			//printf("%d\n", g);
			print(des);
			return 1;
		}
		state &a = p.a;
		int x = pos / 3, y = pos % 3;
		for(int i = 0; i < 4; ++i) {
			int px = x + dx[i], py = y + dy[i];
			if(px < 0 || py < 0 || px >= 3 || py >= 3) continue;
			int pz = px * 3 + py;
			swap(a[pos], a[pz]);
			int code1 = KT(a);
			if(vis[code1]) {
				swap(a[pos], a[pz]);
				continue;
			}
			vis[code1] = 1;
			Pre[code1].dir = dir[i];
			Pre[code1].pre = code;
			q.push(node(a, g + 1, get_h(a), code1, pz));
			swap(a[pos], a[pz]);
		}
	}
	return -1;
}

int main() {
	deal();
	char str[50];
	while(fgets(str, sizeof(str), stdin) != NULL) {
		int ind = 0;
		for(int i = 0; str[i] != '\n'; ++i) {
 			if(str[i] == 'x') st[ind++] = 0;
			else if(str[i] >= '0' && str[i] <= '9') st[ind++] = str[i] - '0';
		}
		// 根据逆序数提前判定是否有解
		//for(int i = 0; i < 9; ++i) printf("%d ", st[i]);
		if(cut(st) % 2 == 1) {
			printf("unsolvable\n");
			continue;
		}
		if( Astar() == -1) printf("unsolvable");
		printf("\n");
	}
	return 0;
}

如有不当之处欢迎指出!

HDU - 1043 A* + 康托 [kuangbin带你飞]专题二的更多相关文章

  1. HDU - 3567 Eight II (bfs预处理 + 康托) [kuangbin带你飞]专题二

    类似HDU1430,不过本题需要枚举X的九个位置,分别保存状态,因为要保证最少步数.要保证字典序最小的话,在扩展节点时,方向顺序为:down, left, right, up. 我用c++提交1500 ...

  2. HDU - 3567 IDA* + 曼哈顿距离 + 康托 [kuangbin带你飞]专题二

    这题难度颇大啊,TLE一天了,测试数据组数太多了.双向广度优先搜索不能得到字典序最小的,一直WA. 思路:利用IDA*算法,当前状态到达目标状态的可能最小步数就是曼哈顿距离,用于搜索中的剪枝.下次搜索 ...

  3. 【算法系列学习三】[kuangbin带你飞]专题二 搜索进阶 之 A-Eight 反向bfs打表和康拓展开

    [kuangbin带你飞]专题二 搜索进阶 之 A-Eight 这是一道经典的八数码问题.首先,简单介绍一下八数码问题: 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的 ...

  4. HDU - 3001 Travelling 状压dp + 三进制 [kuangbin带你飞]专题二

    终于刷完搜索专题了. 题意:给定n个城市,每个城市参观不能超过两次,两个城市之间有道路通过需要花费X,求通过能所有城市的最小花费. 思路:每个城市有三个状态0,1,2,可用三进制存储所有城市的访问状态 ...

  5. HDU - 3533 bfs [kuangbin带你飞]专题二

    看了好久的样例才看懂. 题意:有一个人要从(0,0)走到(n,m),图中有k个碉堡,每个碉堡可以向某个固定的方向每隔t秒放一次炮,炮弹不能穿越另一个碉堡,会被阻挡.人在移动的过程中不会被炮弹打到,也就 ...

  6. HDU - 3085 双向BFS + 技巧处理 [kuangbin带你飞]专题二

    题意:有两只鬼,一个男孩女孩被困在迷宫中,男孩每秒可以走三步,女孩只能1步,鬼可以两步且可以通过墙.问男孩女孩是否可以在鬼抓住他们之前会合? 注意:每秒开始鬼先移动,然后两人开始移动. 思路:以男孩和 ...

  7. HDU - 1067 Gap (bfs + hash) [kuangbin带你飞]专题二

    题意:    起初定28张卡牌的排列,把其中11,  21, 31, 41移动到第一列,然后就出现四个空白,每个空白可以用它的前面一个数的下一个数填充,例如43后面的空格可以用44填充,但是47后面即 ...

  8. HDU - 2102 A计划 (BFS) [kuangbin带你飞]专题二

    思路:接BFS判断能否在限制时间内到达公主的位置,注意如果骑士进入传送机就会被立即传送到另一层,不会能再向四周移动了,例如第一层的位置(x, y, 1)是传送机,第二层(x, y, 2)也是传送机,这 ...

  9. HDU - 2181 dfs [kuangbin带你飞]专题二

    保存每个节点的下一个节点一直往下面走就行了,不能重复经过某个点,当经过的点达到20个而且当前节点的下一个节点是起点就打印答案. AC代码 #include<cstdio> #include ...

随机推荐

  1. Django 中 makemigrations、migrate时 No changes detected

    Django创建的项目中,需要更改.增加.删除表中的某些属性,性急直接把之前数据库表删除了,之后再执行: python manage.py makemigrations python manage.p ...

  2. MoonLight可视化订单需求区域分析系统前端

    MoonLight可视化订单需求区域分析系统实现功能: 在现实生活中,计算机和互联网迅速发展,人们越来越趋向于网络,于是我们就有了各种各样的系统,来帮助我们更好地生活.比如对于打车来说,我们也可以通过 ...

  3. centos 如何关闭防火墙?

    1 查看防火墙状态: 命令: /etc/init.d/iptables status 如果是开着显示内容类是截图 2 临时关闭防火墙: 命令:/etc/init.d/iptables stop     ...

  4. android CheckBox与监听

    <CheckBox  android:id="@+id/cb1"  android:layout_width="fill_parent"  android ...

  5. ie下常见的css兼容问题

    1.border-radius 边框圆角 IE8及以下浏览器不支持border-radius webkit引擎支持-webkit-borderradius 私有属性 mozilla Gecko引擎支持 ...

  6. awk 实用技巧

    awk 用法:awk ' pattern {action} ' 变量名 含义ARGC 命令行变元个数ARGV 命令行变元数组FILENAME 当前输入文件名FNR 当前文件中的记录号FS 输入域分隔符 ...

  7. sed的替换命令

    例1: [root@nhserver2 ~]# cat nagios.txt<TD ALIGN=LEFT valign=center CLASS='statusBGCRITICAL'>&l ...

  8. 【C++】bazel的使用

    bazel的使用 bazel是google开源的构建工具,可以支持多种语言的构建.这里来尝试一下如何在C++项目中使用bazel构建. 安装就不介绍了,在官网很详细,输入bazel --help: U ...

  9. exif.js 旋转图片

    还是那个问题:网上的大胸弟们BB一大堆,没几个给的代码能用的,话不多说,直接上demo: 链接:https://pan.baidu.com/s/1jJ6vnt8 密码:swwm 不要忽视demo中的s ...

  10. BZOJ 4025: 二分图 [线段树CDQ分治 并查集]

    4025: 二分图 题意:加入边,删除边,查询当前图是否为二分图 本来想练lct,然后发现了线段树分治的做法,感觉好厉害. lct做法的核心就是维护删除时间的最大生成树 首先口胡一个分块做法,和hno ...