今天zyb参加一场面试,面试官听说zyb是ACMer之后立马抛出了一道算法题给zyb:
有一个序列,是1到n的一种排列,排列的顺序是字典序小的在前,那么第k个数字是什么?
例如n=15,k=7, 排列顺序为1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9;那么第7个数字就是15.
那么,如果你处在zyb的场景下,你能解决这个问题吗?
Input
T组样例(T<=100)
两个整数n和k(1<=n<=1e6,1<=k<=n),n和k代表的含义如上文
两个整数n和k(1<=n<=1e6,1<=k<=n),n和k代表的含义如上文
Output
输出1-n之中字典序第k小的数字
Sample Input
1
15 7
Sample Output
15
就是求1-n的字典序排列的第几个
一开始用递归来写 果然超时了
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define N 1e6+5 int cnt; int m; void f(int n,int t) { if(n>t) return; ++cnt; if(cnt==m){printf("%d ",n);return;} rep(i,0,9) f(10*n+i,t); } int main() { int n; int cas;RI(cas); while(cas--) { cnt=0; RII(n,m); for(int i=1;i<=9;i++) f(i,n); } }
参考大神的方法:
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// long getCntOfPre(long pre, long n) // 计算以pre为开头并且小于n的数字的个数 { long cnt = 1; long p = 10; for (; pre*p <= n; p *= 10) cnt += min(n, pre*p - 1 + p) - pre*p + 1; return cnt; } long solve(long n, long m) { long ans = 1; while (m != 0) { long cnt = getCntOfPre(ans, n); if (cnt >= m) { m--; if (m == 0) break; // 最终结果 ans *= 10; // 对应打头的数乘10,比如:原来是计算10打头的个数,现在要计算100打头的个数(缩小范围) } else { m -= cnt; // 第m个数改为第m-cnt个数 ans++; // 对应打头的数加1,比如:原来是计算10打头的个数,现在要计算11打头的个数 } } return ans; } int main() { long n, m; int q;RI(q); while (q--) { RII(n,m); cout << solve(n, m) << endl; } return 0; }
标准答案是用十叉树来做