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

题目简述

此处tp至题目

分析

  1. 康托展开

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

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

X=a[n](n1)!+a[n1](n2)!+...+a[i](i1)!+...+a[1]0!

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

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

①对于第一位2,小于2的数字只有1,故a1=1

②对于第二位1,没有小于1的数字,故a2=0;

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

④对于第四位3,没有小于3的数字,故a4=0

所以康托展开值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个数比第一位小,故a1=2;

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

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

④0/0!=0,说明没有数比第四位小,故a4=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 @   SxtoxA  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
12 13
点击右上角即可分享
微信分享提示