HDU 1043(八数码问题)
来源:互联网 发布:用友软件怎么下载 编辑:程序博客网 时间:2024/06/05 15:06
题目大意:多个案例,每次无序的输入九个符号('1'-'8'和'x')。这九个符号按输入顺序依次放入一个3*3的九宫格中。其中,符号'x'可以和上,下,左,右邻居交换位置,题目要求你通过这种交换使得九个符号呈1,2,3,4,5,6,7,8,x排列。要求你输出交换的顺序。(用'u','d','l','r'来表示上下左右)。然后题目是special judge(特判),即解决方法可能有多种,你输出任意一种即可。当达不到题目要求时输出'unsolvable'。
题解:一道八数码的题目,这里介绍其中一个解法:哈希+打表(比较水但简单的解法)。
首先,题目解法是将9个数字('x'看成数字9)的每一种排列看成一种状态,则共有9!种不同的状态。我们从结果状态(1,2,3,4,5,6,7,8,9,)开始bfs搜索,每到达一个状态就将这个状态标记上(一是标记这个状态可以到达结果,二是标记访问了这个状态,防止重复访问,ps:利用一个flag数组来进行标记),标记的同时要记录这个状态到达前一状态的op(‘u,d,r,l’),记住要反向记录,因为我们是反向搜索(从结果开始搜索),此外我们还要记录这一状态的前一状态,这样每次才能从某一状态一直找到结果状态(递归查询)。bfs结束时我们便找到了所有可以到达结果的状态,并且记录下了路径,问题也基本得到解决。
然后说说细节。虽然此问题总共只有9!种状态(大约40万),但是我们要建立每个状态和唯一一个数字的联系,以方便我们的标记,这便要用到哈希。这里有个哈希的小技巧:全排列数化技术。首先,我们介绍一种新的进制方法。一般我们的进制方法都是以某个数k为基数,第n位的单位为k^(n-1),每一位到达k便进位。我们的这种新的进位是变进制数表示法。这种方法第n位的单位为n!,第n位到达n+1时便进位。因为对于任意n>0都有(n+1)!=n!*(n+1),所以这种进位方法完全可行。然后我们说明这种进制与全排列之间的联系。首先,n个数的全排列共有n!种状态,然后n-1位变进制的范围是[0,n!-1],刚好有n!个不同的数,也就是说两个东西的范围是一样的,下面我们来建立它们的一一对应。对于产生的任一排列 c1,c2,..,cn,其中第i个元素ci(2 <= i <= n)与它前面的i个元素构成的逆序对的个数为di(0 <= di < i),那么我们得到一个逆序数序列d1,d2,d3,...,d(n-1)(0 <= di <= i),而这与我们的变进制刚好对应(因为第i位满足di <= i)。于是我们可以用k=d1*1!+d2*2!+...+d(n-1)*(n-1)!来表示这个状态,并且对于任意不同的排列c1,c2,..,cn必有不同的逆序数序列d1,d2,d3,...,d(n-1)。证明如下:
对于全排列的任意两个不同的排列p0,p1,p2,...,pn(排列P)和q0,q1,q2,...,qn(排列Q),从后往前查找第一个不相同的元素,分别记为pi和qi(0 < i <= n)。
(1)如果qi > pi,那么,
如果在排列Q中qi之前的元素x与qi构成逆序对,即有x > qi,则在排列P中pi之前也有相同元素x > pi(因为x > qi且qi > pi),即在排列P中pi之前的元素x也与pi构成逆序对,所以pi的逆序数大于等于qi的逆序数。又qi与pi在排列P中构成pi的逆序对,所以pi的 逆序数大于qi的逆序数。
(2)同理,如果pi > qi,那么qi的逆序数大于pi的逆序数。
因此,由(1)和(2)知,排列P和排列Q对应的变进制数至少有第i位不相同,即全排列的任意两个不同的排列具有不同的变进制数。至此得证。
至此,我们可以获取任意排列的哈希值。
下面是代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<string>#include<queue>#define ll long longusing namespace std;const int inf=0x08080808;const int maxn=4e5;char str[40],op[maxn],cc[4]={'u','d','l','r'};int fa[10],flag[maxn],nn[maxn];int mo[4][2]={1,0,-1,0,0,1,0,-1};struct digital{ int num[10]; int pos;};int hash_val(int num[]){ int ans=0; for(int i=1;i<9;++i){ int icount=0; for(int j=0;j<i;++j) if(num[j]>num[i]) ++icount; ans+=icount*fa[i]; } return ans;}queue<digital>q;bool is_norm(int x,int y,int mx,int my){ if(x+mx>=0&&x+mx<3&&y+my>=0&&y+my<3) return true; return false;}void bfs(){ digital temp; for(int i=0;i<9;++i) temp.num[i]=i+1; temp.pos=8; int ha=hash_val(temp.num),hp; flag[ha]=1; op[ha]='\0'; q.push(temp); while(!q.empty()){ temp=q.front(); q.pop(); int pos=temp.pos; hp=hash_val(temp.num); int x=pos/3,y=pos%3; for(int i=0;i<4;++i){ if(is_norm(x,y,mo[i][0],mo[i][1])){ int new_pos=(x+mo[i][0])*3+y+mo[i][1]; digital t; for(int j=0;j<9;++j) t.num[j]=temp.num[j]; t.pos=new_pos; t.num[pos]=temp.num[new_pos]; t.num[new_pos]=temp.num[pos]; ha=hash_val(t.num); if(flag[ha]==0){ flag[ha]=1; nn[ha]=hp; op[ha]=cc[i]; q.push(t); } } } }}int main(){ fa[0]=1; for(int i=1;i<9;++i) fa[i]=fa[i-1]*i; memset(flag,0,sizeof(flag)); bfs(); while(scanf("%c",&str[0])!=EOF){ int num[10],ha; if(str[0]=='x') num[0]=9; else num[0]=str[0]-'0'; for(int i=1;i<9;i++){ cin>>str[i]; if(str[i]=='x') num[i]=9; else num[i]=str[i]-'0'; } ha=hash_val(num); if(flag[ha]){ while(op[ha]){ printf("%c",op[ha]); ha=nn[ha]; } printf("\n"); } else printf("unsolvable\n"); getchar(); } return 0;}
- 八数码问题(HDU 1043)
- HDU 1043(八数码问题)
- hdu 1043(八数码问题)
- HDU:1043 八数码问题
- 八数码(hdu 1043)
- hdu 1043 eight 八数码问题
- HDU 1043 八数码问题 A*搜索
- HDU-1043 Eight 八数码问题
- HDU 1043 八数码问题 A*搜索
- 八数码问题——HDU 1043
- hdu 1043 八数码问题-A*搜索
- hdu 1043 Eight 经典八数码问题
- HDU 1043 八数码
- hdu 1043 Eight(八数码)
- HDU 1043 八数码(A*搜索)
- HDU 1043 eight 八数码
- HDU-1043-八数码-代码
- hdu 1043 八数码 A*
- quartz_job_trigger
- 低通采样和带通采样定理
- vue 滑动组件
- this--Java基础041
- dp入门
- HDU 1043(八数码问题)
- 按开源项目风格构建部署自己的项目
- LeetCode - 162. Find Peak Element - 思路详解 - C++
- Android 6.0 权限解析
- HDU 5297 迭代
- 对dispatch的一点理解
- 关于MongoDB相关安装
- String to Integer (atoi)
- 【LeetCode】 435. Non-overlapping Intervals