康托展开...第一遍看到这个名称的时候我是一脸懵逼的...(康托?康熙几世孙?)
其实它的主要作用就是压缩判重量...
举个简单的栗子...我们要判断的内容实质是9的全排列...
如果直接开数组就是[123456789..987654321](当然如果你倔强地要从0开始我也不拦着...)
一算8亿多,炸内存...预计威力达到学校机房烂掉...
我知道热爱世界和平并且希望有个好成绩的你不会愿意这样子的...
仔细想想其中有很多是浪费掉的,比如198307177这种有重复数字的,不可能出现...
所以康托展开就横空出世了...
公式是这个样子...
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!
它求得的实际上是某个排列数的编号...
比如123456789对应的是0,123456798对应的是1,987654321对应的是362879...
这样的话就剩下9!=362880种,存储空间还可以留一部分给你住...(开玩笑...)
具体原理跟那个NOIP2014普及第四题的火星人中“求下一个排列数”类似...
康托展开就是把那一题倒了一下,从求第n个排列数变成了求某个排列数是第几个...
上文引用自洛谷一篇题解P2578
公式:
x的编码=∑i=1n(ai−bi−1)×(n−i)!
给出代码:
#include<cstdio>
#include<iostream>
#define N 10
using namespace std;
int a[N],n,c[N];
bool b[N];
int Contor(int x){
int num=0,cnt,ans=0;
while(x!=0){
a[++num]=x%10;
x/=10;
}
for(int i=num;i>=1;i--){
cnt=0;
for(int j=num;j>=i+1;j--)if(a[j]<a[i])cnt++;
ans+=(a[i]-cnt-1)*c[i-1];
}
return ans;
}
void dfs(int x,int y){
if(x==n+1){
printf("%d %d\n",y,Contor(y));
return;
}
for(int i=1;i<=n;i++){
if(!b[i]){
b[i]=1;
dfs(x+1,y*10+i);
b[i]=0;
}
}
}
int main(){
c[0]=1;
for(int i=1;i<=9;i++)c[i]=c[i-1]*i;
scanf("%d",&n);
dfs(1,0);
}