当前位置: 首页 > news >正文

HDU-1043 Eight(经典八数码问题, A*+康拓+曼哈顿距离+逆序数判断可解性、双向搜索)

题意:

给定一个序列,由1~8数字和字母x组成,表示的是一个3*3的矩形。每次操作x都能与相邻的数字交换,问如何操作才能使得序列为{1,2,3,4,5,6,7,8,x}。

思路:

经典八数码问题,寻找解,肯定是越少的操作越有可解性。所以利用康拓展开将字符串hash成数值之后,很容易就能想出暴力广搜的思路,但是肯定会超时,因为状态共9!个,广搜稍微一扩散就爆炸了。优化搜索都会想用A*,可是估价函数不大好找啊,一个稍简单的估价函数:计算当前状态与目标状态的同一位置不同数字的个数。但是仍然超时。网上大多数代码都用到了曼哈顿距离去估价,其实确实是可行的,这么估价肯定是小于等于真正代价的。但是还不够AC...网上大多数代码又用到了利用这个序列逆序数的奇偶性进行判断游戏能否可解,即当且仅当初始状态逆序对个数与目标状态逆序对个数奇偶性相同才有解。想不大懂orz...(证明链接)

另外还可以用双向搜索做。双向广度优先算法从两个方向以广度优先的顺序同时扩展,一个是从起始节点开始扩展,另一个是从目的节点扩展,直到一个扩展队列中出现另外一个队列中已经扩展的节点,也就相当于两个扩展方向出现了交点,那么此时可以认为我们找到了一条路径。
双向广度优先算法相对于广度优先算法来说,由于采用了从两个根开始扩展的方式,搜索树的深度得到了明显的减少,所以在算法的时间复杂度和空间复杂度上都有较大的优势!双向广度优先算法特别适合于给出了起始节点和目的节点,要求它们之间的最短路径的问题。其正确性是根据:广度优先的顺序能够保证找到的路径就是最短路径!

双向搜索可以缩短很长的时间。例如一个迷宫,我们假设没有边界,没有墙的空地,单BFS需要4^n时间,而双向BFS就是4^(n/2)*2,缩短了很多。


A*代码:

//#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 4e5;
int nxt[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char pnt[] = "rdlu";
struct node
{
	int posx, posy, t, h;
	char s[3][3];
	bool operator<(const node k)const
	{
		return t+h > k.t+k.h;
	} 
} x;
priority_queue<node> q;
//unordered_map<string, int> has;
//用哈希map优化常数竟然比不优化还耗时... 
int bas[10];
int ans[maxn], pre[maxn];
char ch, sa[maxn];
bool vis[maxn];
void init()
{
	bas[0] = 1;
	for(int i = 1; i < 9; ++i)
	bas[i] = bas[i-1]*i;
}
int H()	//估价函数:计算曼哈顿距离 
{
	int res = 0;
	for(int i = 0; i < 9; ++i)
	if(x.s[i/3][i%3] != '8')
	res += abs(i/3-(x.s[i/3][i%3]-'0')/3)+abs(i%3-(x.s[i/3][i%3]-'0')%3);
	return res;
}
bool isok()	//通过逆序数的奇偶性判断游戏可解性 
{
	int res = 0;
	for(int i = 0; i < 9; ++i)
	for(int j = i+1; j < 9; ++j)
	{
		if(x.s[i/3][i%3] != '8' && x.s[j/3][j%3] != '8' &&
		x.s[i/3][i%3] > x.s[j/3][j%3])
		++res;
	}
	return !(res&1);
}
int getNo()
{
	//string s = "";
	//for(int i = 0; i < 9; ++i) s += x.s[i/3][i%3];
	//if(has.find(s) != has.end()) return has[s];
	int res = 0;
	for(int i = 0; i < 9; ++i)
	{
		int tmp = 0;
		for(int j = i+1; j < 9; ++j)
		if(x.s[j/3][j%3] < x.s[i/3][i%3]) ++tmp;
		res += tmp*bas[8-i];
	}
	//has[s] = res;
	return res;
}
void print()
{
	int cnt = 0, k = 0;
	while(ans[k] != -1)
	{
		sa[cnt++] = pnt[ans[k]];
		k = pre[k];
	}
	sa[cnt] = '\0';
	reverse(sa, sa+cnt);
	printf("%s\n", sa);
}
void Astar()
{
	//has.clear();
	memset(vis, 0, sizeof vis);
	while(!q.empty())
	{
		x = q.top(); q.pop();
		int uid = getNo();
		if(uid == 0)
		{
			print(); return;
		}
		for(int i = 0; i < 4; ++i)
		{
			int tx = x.posx + nxt[i][0];
			int ty = x.posy + nxt[i][1];
			if(tx < 0 || tx >= 3 || ty < 0 || ty >= 3) continue;
			int tposx = x.posx, tposy = x.posy;
			swap(x.s[x.posx][x.posy], x.s[tx][ty]);
			int vid = getNo();
			if(!vis[vid]) 
			{
				vis[uid] = true;
				x.posx = tx, x.posy = ty;
				ans[vid] = i, pre[vid] = uid;
				x.h = H(), ++x.t;
				q.push(x);
				--x.t;
				x.posx = tposx, x.posy = tposy;
			}
			swap(x.s[x.posx][x.posy], x.s[tx][ty]);
		}
	}
}
int main()
{
	init();
	//freopen("in.txt", "r", stdin);
	while(scanf(" %c", &ch) != EOF)
	{
		if(ch != 'x') x.s[0][0] = ch-1;
		else x.s[0][0] = '8', x.posx = 0, x.posy = 0;
		for(int i = 1; i <= 8; ++i)
		{
			scanf("  %c", &ch);
			if(ch != 'x') x.s[i/3][i%3] = ch-1;
			else x.s[i/3][i%3] = '8', x.posx = i/3, x.posy = i%3;
		}
		int sid = getNo();
		pre[sid] = -1, ans[sid] = -1;
		x.h = H(), x.t = 0;
		while(!q.empty()) q.pop();
		q.push(x); vis[sid] = true;
		if(!isok() && sid != 0) puts("unsolvable");
		else Astar();
	}
	return 0;
}


双向bfs代码:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 4e5;
int nxt[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char pnt[] = "rdlu";
struct node
{
	int posx, posy, t;
	char s[3][3];
} x;
queue<node> q[2];
int bas[10];
int ans[2][maxn], pre[2][maxn];
char ch, sa[maxn];
bool vis[2][maxn], flag;
void init()
{
	bas[0] = 1;
	for(int i = 1; i < 9; ++i)
	bas[i] = bas[i-1]*i;
}
bool isok()
{
	int res = 0;
	for(int i = 0; i < 9; ++i)
	for(int j = i+1; j < 9; ++j)
	{
		if(x.s[i/3][i%3] != '8' && x.s[j/3][j%3] != '8' &&
		x.s[i/3][i%3] > x.s[j/3][j%3])
		++res;
	}
	return !(res&1);
}
int getNo()
{
	int res = 0;
	for(int i = 0; i < 9; ++i)
	{
		int tmp = 0;
		for(int j = i+1; j < 9; ++j)
		if(x.s[j/3][j%3] < x.s[i/3][i%3]) ++tmp;
		res += tmp*bas[8-i];
	}
	return res;
}
void print(int k)
{
	int cnt = 0, t = k;
	while(ans[0][t] != -1)
	{
		sa[cnt++] = pnt[ans[0][t]];
		t = pre[0][t];
	}
	reverse(sa, sa+cnt);
	t = k;
	while(ans[1][t] != -1)
	{
		sa[cnt++] = pnt[(ans[1][t]+2)%4];
		t = pre[1][t];
	}
	sa[cnt] = '\0';
	printf("%s\n", sa);
}
void bfs(int id)
{
	x = q[id].front(); q[id].pop();
	int uid = getNo();
	for(int i = 0; i < 4; ++i)
	{
		int tx = x.posx + nxt[i][0];
		int ty = x.posy + nxt[i][1];
		if(tx < 0 || tx >= 3 || ty < 0 || ty >= 3) continue;
		int tposx = x.posx, tposy = x.posy;
		swap(x.s[x.posx][x.posy], x.s[tx][ty]);
		int vid = getNo();
		if(!vis[id][vid]) 
		{
			vis[id][vid] = true;
			x.posx = tx, x.posy = ty;
			ans[id][vid] = i, pre[id][vid] = uid;
			++x.t;
			if(vis[id^1][vid])
			{
				flag = true;
				print(vid); return;
			}
			q[id].push(x);
			--x.t;
			x.posx = tposx, x.posy = tposy;
		}
		swap(x.s[x.posx][x.posy], x.s[tx][ty]);
	}
}
void work()
{
	while(!q[0].empty()) q[0].pop();
	while(!q[1].empty()) q[1].pop();
	memset(vis, 0, sizeof vis);
	
	int sid = getNo();
	pre[0][sid] = -1, ans[0][sid] = -1;
	x.t = 0; q[0].push(x);
	vis[0][sid] = true;
	
	for(int i = 0; i < 9; ++i) x.s[i/3][i%3] = i+'0';
	pre[1][0] = -1, ans[1][0] = -1;
	x.posx = 2, x.posy = 2;
	x.t = 0; q[1].push(x);
	vis[1][0] = true;
	
	flag = false;
	while(!q[0].empty() && !q[1].empty())
	{
		bfs(0);
		if(flag) break;
		bfs(1);
		if(flag) break;
	}
} 
int main()
{
	init();
	//freopen("in.txt", "r", stdin);
	while(scanf(" %c", &ch) != EOF)
	{
		if(ch != 'x') x.s[0][0] = ch-1;
		else x.s[0][0] = '8', x.posx = 0, x.posy = 0;
		for(int i = 1; i <= 8; ++i)
		{
			scanf("  %c", &ch);
			if(ch != 'x') x.s[i/3][i%3] = ch-1;
			else x.s[i/3][i%3] = '8', x.posx = i/3, x.posy = i%3;
		}
		if(!isok() && getNo() != 0) puts("unsolvable");
		else work();
	}
	return 0;
}


继续加油~

相关文章:

  • codeforces-510E Fox And Dinner(带限制的二分图多重匹配+奇偶建图+打印路径)
  • C-Cleaning Pipes(判断两线段相交+二分图判定) 2015-2016 Northwestern European Regional Contest (NWERC 2015)
  • eclipse the user operation is waiting for building workspace to complete
  • 2-SAT 题目整理
  • 64位windows2003 未在本地计算机上注册 microsoft.jet.oledb.4.0 提供程序
  • HDU-5950 Recursive sequence(矩阵乘法)
  • “互联网+”引发IT人才招工荒-新华网安徽频道
  • Java从文件读入以及读出至文件
  • HDU-5963 朋友(树上博弈)
  • HDU 6005 Pandaland(无向图最小环)
  • Cura源码在Ubuntu15.04上编译脚本(成功)
  • SPOJ - CHICAGO 106 miles to Chicago(乘积最短路)
  • HTML5之FileReader的使用
  • LeetCode 202 Happy Number(floyd判圈算法(龟兔赛跑算法))
  • css3 中text-align:justify
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • CSS相对定位
  • JavaScript创建对象的四种方式
  • LeetCode18.四数之和 JavaScript
  • Python中eval与exec的使用及区别
  • v-if和v-for连用出现的问题
  • 缓存与缓冲
  • 排序算法学习笔记
  • 判断客户端类型,Android,iOS,PC
  • 如何进阶一名有竞争力的程序员?
  • 世界上最简单的无等待算法(getAndIncrement)
  • 责任链模式的两种实现
  • 第二十章:异步和文件I/O.(二十三)
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​Python 3 新特性:类型注解
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #Linux(帮助手册)
  • (39)STM32——FLASH闪存
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (待修改)PyG安装步骤
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)WCF的Binding模型
  • (算法)前K大的和
  • (正则)提取页面里的img标签
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .net core使用ef 6
  • .Net Core与存储过程(一)
  • .NET开发人员必知的八个网站
  • .php文件都打不开,打不开php文件怎么办
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [20180129]bash显示path环境变量.txt
  • [android] 练习PopupWindow实现对话框
  • [C++]指针与结构体
  • [CF407E]k-d-sequence
  • [COI2007] Sabor
  • [CSS]中子元素在父元素中居中
  • [dfs搜索寻找矩阵中最长递减序列]魔法森林的秘密路径
  • [leetcode]Symmetric Tree