HDU - 1043 Eight 【打表、康托展开、反向bfs】
题目简述
分析
- 康托展开
对于一个有n!种排列方式的排列组合,康托展开表示的就是是当前排列组合在全排列中的位次。
我们常构建下式来表示康托展开值:
其中表示比第i位数字小且前面未出现的数字个数。
举个例子:对于集合{1,2,3,4},我们取其中一种排列(2 1 4 3)来分析。
①对于第一位2,小于2的数字只有1,故;
②对于第二位1,没有小于1的数字,故;
③对于第三位4,小于4的数字只有3,因为1和2出现在了第二位和第一位,故;
④对于第四位3,没有小于3的数字,故。
所以康托展开值,即比(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;
} //康托展开
- 逆康托展开
康托展开实质是建立全排列与自然数的双射,故而是可逆的。
还是以(2 1 4 3)为例,假设我们已知由1234组成的某个全排列,其康托展开值为7,那我们可以进行逆运算:
①7/3!=1余1,说明有1个数比第一位小,故;
②1/2!=0余1,说明没有数比第二位小,故;
③1/1!=1余0,说明有1个数比第三位小,故(算上已出现的1和2,再加一位);
④0/0!=0,说明没有数比第四位小,故。
故逆康托展开的结果是(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;
}//逆康托展开
- bfs过程
首先我们需要确定的是,因为已知终点,故逆序bfs是优于从起点开始的正序的(后者实测)
在每次遍历到新位置时,我们将其与上一个状态的位置交换,将的康托展开值进行更新,同时记录轨迹(将康托展开值变为下标)。
注意G++会炸,转C++提交(手动流汗黄豆)。
AC代码参考:https://blog.csdn.net/laaahu/article/details/96467188
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!