[POJ 1715] Hexadecimal Numbers 求排列数/数位DP

来源:互联网 发布:form表单转json 编辑:程序博客网 时间:2024/06/08 01:14

题目传送门:【POJ 1715】


题目大意: 输入一个正整数 n,求第 n 大的不超过 8 位的各位数字不同的 16 进制数。注意,最后得到的 16 进制数不含前导 0(即:前面的 0 可以重复)。保证输入合法。

样例输入:11
样例输出:FEDCBA87


题目分析:
(mmp浪费时间浪费生命的辣鸡排列组合题,又耗我一下午)

由题,这道题看起来可以根据 n 与每一位数的关系暴力求出每一位数的值,但这样做十分不方便,特别是最后判前导 0 的时候很不好写,所以不推荐这样做(不过我 AC 了,100 行+,不在这里贴出来)

如果想看更详细的,这里参考了 Hacker_vision的题解。

首先我们先要求出这个 n 能达到的那个数的字符串长度。
然后我们对每一位数进行枚举,求出符合的最大的数 ( 0 - F ),同时输出这一位数代表的 0-F 即可。这里需要用到排列的性质。
至于为什么说是数位DP呢……因为这道题是对每一位数进行处理……所以就是数位DP了……妙不妙……


下面附上代码:

[cpp] view plain copy
print?
  1. #include<cstdio>  
  2. const char hexa[17]=“0123456789ABCDEF”;  
  3.   
  4. long long tot[10],f[17];                    //tot[i]:i位数的总方案数   
  5. int n,len;  
  6. bool used[17];                              //记录这个数字(0-F)是否被使用   
  7.   
  8. void fac(){                                 //阶乘,求排列用   
  9.     f[0]=1;  
  10.     for (int i=1;i<=15;i++) f[i]=f[i-1]*i;  
  11. }  
  12. long long A(int a,int b){return f[a]/f[a-b];}  
  13. void preprocess(){  
  14.     fac();  
  15.     for (int i=8;i>0;i–)  
  16.         tot[i]=tot[i+1]+A(15,i-1)*15;  
  17.     tot[1]++;                               //仅含 0   
  18. }  
  19.   
  20. int main(){  
  21.     preprocess();  
  22.     scanf(”%d”,&n);  
  23.     for (len=8;len>0;len–) if (n<=tot[len]) break;  
  24.     long long left=n-tot[len+1];            //left:剩余数的数量   
  25.     for (int i=len;i>0;i–){  
  26.         for (int k=15;k>=0;k–){         //枚举这个数(0-F)能不能填   
  27.             if (used[k]) continue;   
  28.             long long tmp=A(15-len+i,i-1);  
  29.             if (left<=tmp){                  //剩余的数不多于tmp,直接选   
  30.                 printf(”%c”,hexa[k]);  
  31.                 used[k]=true;  
  32.                 break;  
  33.             }  
  34.             left-=tmp;  
  35.         }  
  36.     }  
  37.     return 0;  
  38. }  
原创粉丝点击