HDU - 1043 Eight 【打表、康托展开、反向bfs】

题目简述

此处tp至题目

分析

  1. 康托展开

对于一个有n!种排列方式的排列组合,康托展开表示的就是是当前排列组合在全排列中的位次。

我们常构建下式来表示康托展开值:

\[X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! \]

其中\(a_i\)表示比第i位数字小且前面未出现的数字个数。

举个例子:对于集合{1,2,3,4},我们取其中一种排列(2 1 4 3)来分析。

①对于第一位2,小于2的数字只有1,故\(a_1=1\)

②对于第二位1,没有小于1的数字,故\(a_2=0\);

③对于第三位4,小于4的数字只有3,因为1和2出现在了第二位和第一位,故\(a_3=1\);

④对于第四位3,没有小于3的数字,故\(a_4=0\)

所以康托展开值\(\texttt{x=1*3!+0*2!+1*1!+0*0!=7}\),即比(2 1 4 3)小的排列有7个,故(2 1 4 3)排第8名。

代码如下:

int cantor(int a[],int n){
	int x=0;
	for(int i=0;i<n;i++){
		int small=0;
		for(int j=i+1;j<n;j++){
			if(a[j]<a[i]) small++;
		}
		x+=small*fact[n-i-1];
	}
	return x+1;
} //康托展开 
  1. 逆康托展开

康托展开实质是建立全排列与自然数的双射,故而是可逆的。

还是以(2 1 4 3)为例,假设我们已知由1234组成的某个全排列,其康托展开值为7,那我们可以进行逆运算:

①7/3!=1余1,说明有1个数比第一位小,故\(a_1=2\);

②1/2!=0余1,说明没有数比第二位小,故\(a_2=1\)

③1/1!=1余0,说明有1个数比第三位小,故\(a_3=4\)(算上已出现的1和2,再加一位);

④0/0!=0,说明没有数比第四位小,故\(a_4=3\)

故逆康托展开的结果是(2 1 4 3)。

一般化上述过程,即可得到对应代码:

void recantor(int a[],int val,int n){
	vector<int> v;
	for(int i=0;i<n;i++) v.push_back(i);
	for(int i=0;i<n;i++){
		int l=0,r=0;
		l=val/fact[n-i-1];
		r=val%fact[n-i-1];
		val=r;
		sort(v.begin(),v.end());
		a[i]=v[l];
		v.erase(v.begin()+l);
	}
	return;
}//逆康托展开 
  1. bfs过程

首先我们需要确定的是,因为已知终点,故逆序bfs是优于从起点开始的正序\(bfs\)的(后者实测\(MLE\)

在每次遍历到新位置\(temp\)时,我们将其与上一个状态\(curr\)的位置交换,将\(temp\)的康托展开值进行更新,同时记录轨迹(将康托展开值变为下标)。

注意G++会炸,转C++提交(手动流汗黄豆)。

AC代码参考:https://blog.csdn.net/laaahu/article/details/96467188

posted @ 2022-07-18 15:03  SxtoxA  阅读(21)  评论(0)    收藏  举报
12 13