1.组合数学。
强调不一样的是,组合数学以数作为逆序的依据,bk表示与数k逆序的数有多少个0<=bk<=n-k
因此转换的公式是:
sum=bn*(0)!+bn-1*(1)!+……+bk*(n-k)!+……+b2*(n-2)!+b1*(n-1)!
bn=0恒成立
sum最大值为当bk取n-k得最大值n!-1最小值为0,共n!种状态,与n!种排列一一对应。
解码时bk放在第bk+1个空位置上,原理就不解释了,太长了,书上有。
2.还可以参考http://bbs.chinaunix.net/viewthread.php?tid=1283459作为资料。
这里的依据是以下标di表示下标为i的数与前面互逆的数的个数,因此0<=di<=i;
公式为:
sum=d0*(0)!+d1*(1)!+……+di*(i)!+……dn-1*(n-1)!+dn*(n)!
d0=0恒成立
3.附上以组合数学上为原理的代码
#include<cstdio> #include<iostream> using namespace std; int p[9]= {1,1,2,6,24,120,720,5040,40320}; int encode(int *perm)//n=9 { //八数码问题,九个格子,相当于总的排列数为9! int sum=0; for(int i=0; i<9; i++) { int count=0; for(int j=0; j<i; j++) { if(perm[j]>perm[i])count++; } sum+=count*p[9-perm[i]]; } return sum; } void decode(int sum,int *perm) { for(int i=0; i<9; i++)perm[i]=0; for(int i=1; i<=9; i++) { int tot=sum/p[9-i]+1; sum%=p[9-i]; int loc,count=0; for(loc=0; loc<9; loc++) { if(!perm[loc]) { count++; if(count==tot)break;//!!找到空第tot个空位置 } } perm[loc]=i; } } int main() { freopen("data.in","r",stdin); int a[10]; for(int i=0;i<9;i++)cin>>a[i];//1,2,3,4,5,6,7,8,9 int t[10]; decode(encode(a),t);//编码后解码,若得原码则可认为是正确的 for(int i=0; i<9; i++)cout<<t[i]<<' '; cout<<endl; return 0; }
#include<cstdio> #include<iostream> using namespace std; int p[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320}; int encode(int* s){//升序做为正序 int x=0; int i,j; for(i=0;i<9;i++){ int k =s[i]; for(j=0;j<i;j++) if(s[j]<s[i])k--; x+=k*p[8-i]; } return x; } void decode(int x,int* s){ int i,j; for(i=0; i<9;i++){ s[i]=x/p[8-i]; x%=p[8-i]; } for(i=8;i>=0;i--) for(j=8;j>i;j--) if(s[j]>=s[i])s[j]++; } int main() { freopen("data.in","r",stdin); int a[10]; for(int i=0;i<9;i++)cin>>a[i];//0,1,2,3,4,5,6,7,8 int t[10]; decode(encode(a),t);//编码后解码,若得原码则可认为是正确的 for(int i=0; i<9; i++)cout<<t[i]<<' '; cout<<endl; return 0; }