/* 基本知识:给每一个全排列一个号: {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。 代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。 他们间的对应关系可由康托展开来找到。 如我想知道321是{1,2,3}中第几个大的数可以这样考虑 : 第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个大的数。 2*2!+1*1!是康托展开。 再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。 首先看几个康托展开的实例(9的全排列): 1 2 3 4 5 6 7 8 9——展开为 0。 1 2 3 4 5 6 7 9 8——展开为 1。 1 2 3 4 5 6 8 7 9——展开为 2。 由这些最开始的方法我们可以发现一个规律:从第一个数开始,依次判断判断这些数是当前没有出现过的数的第几个(由0开始),记为a1, a2, ... ,a(n - 1)。不难发现如1 2 3 4 5 6 8 7 9,由1至6都是当前没有出现过的第0个数,而8是7,8,9中的第1个(由0开始),9是7,9中的第1个,7是第0个。故a1 = a2 = ... = a6 = 0,a7 = 1,a8 = 1,a9 = 0。 之后排列数(康托展开的值)等于 a1 * (n - 1)! + a2 * (n - 2)! + ... + ak * (n - k)! + ... + an * 0! 再举几个例子: 3 5 7 4 1 2 9 6 8——展开为 98884。 5 6 7 8 1 2 3 4 9——展开为 184800。 往回转换也很简单,分步取模就可以了,在此就不赘述了。搜索采用单向BFS,足够满足时间需求。 */ #include
#include
#define NUM 362880 int queue[NUM],pre[NUM]; char dir[NUM]; int fac[9]={1, 1, 2, 6, 24, 120, 720, 5040, 40320}; //给你一个排列,给我求出是第几个(基本的组合知识) int pack(int *a) { int ans=0,i,j,r; char p[10]={0}; for(i=0;i<9;i++) { for(j=1,r=0;j<=a[i];j++) if(p[j]==0)r++; ans+=(r-1)*fac[8-i]; p[a[i]]=1; } return ans; } //给你时第几个排列,求出排列是什么 void unpack(int s,int *a) { int i,j,r,t; char p[10]={0}; for(i=0;i<9;i++) { t=s/fac[8-i]+1; s%=fac[8-i]; r=0;j=1; while(1) { if(p[j]==0) r++; if(r==t) break; j++; } a[i]=j; p[j]=1; } return; } //普及C语言基础,忘了怎么交换,看了书才知道 void chang(int *a,int *b) { int tmp; tmp=*a; *a=*b; *b=tmp; } int main() { char ch[2]; int i,ori,ss[9],flag=0; int cur,temp,p[9],l; for(i=0;i<9;i++)//输入 { scanf("%s",ch); if(ch[0]=='x') ss[i]=9; else ss[i]=ch[0]-48; } memset(pre,-1,sizeof(pre)); ori=pack(ss); int s=0; int t=0; queue[t++]=0; pre[0]=1; dir[0]=0; while(s!=t)//队列 { cur=queue[(s++)%NUM]; unpack(cur,p); for(i=0;i<9;i++) { if(p[i]==9) { temp=i; break; } } //下面四步很经典,分别是如果不是最上边一行就可以往下走,如果不是最下边一行就可以往上走 //同理,左右一样 //由于输出时倒着输出,所以干好反过来 if(temp!=0&&temp!=3&&temp!=6) { chang(&p[temp],&p[temp-1]); l=pack(p); if(pre[l]==-1) { pre[l]=cur; dir[l]='r'; queue[(t++)%NUM]=l; if(l==ori) { flag=1; break; } } chang(&p[temp],&p[temp-1]); } if(temp!=2&&temp!=5&&temp!=8) { chang(&p[temp],&p[temp+1]); l=pack(p); if(pre[l]==-1) { pre[l]=cur; dir[l]='l'; queue[(t++)%NUM]=l; if(l==ori) { flag=1; break; } } chang(&p[temp],&p[temp+1]); } if(temp!=0&&temp!=1&&temp!=2) { chang(&p[temp],&p[temp-3]); l=pack(p); if(pre[l]==-1) { pre[l]=cur; dir[l]='d'; queue[(t++)%NUM]=l; if(l==ori) { flag=1; break; } } chang(&p[temp],&p[temp-3]); } if(temp!=6&&temp!=7&&temp!=8) { chang(&p[temp],&p[temp+3]); l=pack(p); if(pre[l]==-1) { pre[l]=cur; dir[l]='u'; queue[(t++)%NUM]=l; if(l==ori) { flag=1; break; } } chang(&p[temp],&p[temp+3]); } } if (flag==0) printf("unsolvable/n"); else { i=ori; while (i!=0) { printf("%c",dir[i]); i=pre[i]; } printf("/n"); } return 0; } 一下是转的,留作以后有时间慢慢看 //A* C:/Users/Friendy/Desktop/八数码 poj 1077 广搜 A IDA - Andy - 博客园.mht
poj1077
最新推荐文章于 2020-03-18 15:43:01 发布