POJ_1715,过程模拟问题,要求输出第N个大的8位无重复数字的16进制数。
思路:从高位开始,从大到小计算该位填某一数字能有多少种可能(排列组合), 用N减去所有可能数从而找到其对应数值。注意首位是0时要去0。代码有所参考。
#include <stdio.h>
#include <string.h>
bool vis[20]; //16位,表示每个数字是否出现过了
int res[10]; //8位,表示结果的第几位分别是什么数字
long long fac(int n,int m){
//fac(n,m)=A(n,m)=n(n-1)(n-2)……(n-m+1)= n!/(n-m)!
if(m==0) return 1;
int t=n;
long long cnt=1;
while(1){
cnt=cnt*t;
if(t==n-m+1) break;
t--;
}
return cnt;
}
void solve(int n){
int ud=0; //已经确定的位数
bool flag=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=8;i++){ //从高位找起
int t=15; //从高找起
while(t>=1){
if(!vis[t]){ //每个数只能出现一次
long long tmp=fac(16-1-ud,8-i); //高位已经确定时,剩下的组合有tmp种
if(tmp<n) //若组合数还小于当前n的话则减去tmp继续算该位等于t--时的个数
n=n-tmp;
else{ //否则该位就是t,标记跳出
vis[t]=1;
break;
}
}
t--;
}
res[i]=t;
if(t!=0||flag) ud++; //首位去0
if(t!=0) flag=1;
}
}
void print(){
bool flag=0;
for(int i=1;i<=8;i++){
if(res[i]!=0||flag){
if(res[i]<10) printf("%d",res[i]);
else printf("%c",'A'+res[i]-10);
flag=1;
}
}
}
int main(){
int n;
scanf("%d",&n);
solve(n);
print();
return 0;
}