Leetcode - 最小好进制

87 阅读2分钟

这是我参与更文挑战的第18天,活动详情查看更文挑战

题目描述

对于给定的整数 n, 如果nkk>=2)进制数的所有数位全为1,则称 kk>=2)是 n 的一个好进制。

以字符串的形式给出 n, 以字符串的形式返回 n 的最小好进制。

示例 1:

输入:"13"
输出:"3"
解释:133 进制是 111

示例 2:

输入:"4681"
输出:"8"
解释:46818 进制是 11111

示例 3:

输入:"1000000000000000000"
输出:"999999999999999999"
解释:1000000000000000000999999999999999999 进制是 11

提示:

  • n的取值范围是 [3,1018][3, 10^{18}]
  • 输入总是有效且没有前导 0。

解题思路

n 可以表示成位数为 s+1s+1,进制为 k 的数,即 (n)10=(1111)k(n)_{10}=(11…11)_{k},其中 (a)b(a)_b表示 bb 进制的数 aa。那么根据任意进制转换为十进制的方法,我们有:

(1111)k=ks+ks1+ks2++k+1=n(11…11)_k=k^s+k^{s−1}+k^{s−2}+⋯+k+1=n

根据上面的等式,在 s2s \geq 2 时,显然有 n>ksn>k^s,并且根据二项式定理可以得到 n<(k+1)sn<(k+1)^s,因此我们得到了解决这题的关键不等式:

s2,ks<n<(k+1)s∀s≥2,k^s<n<(k+1)^s

将两边同时开 ss 次方,得到:

k<n1/s<(k+1)k<n^{1/s}<(k+1)

这样当 s2s \geq 2 时,n1/sn^{1/s} 的整数部分即为 kk 的值。 由于题目的限制条件,我们只需要在 [2,59][2,59] 的范围内枚举 ss 即可

代码

C++代码

#define LL long long

class Solution {
public:
    string smallestGoodBase(string N) {
        // (11...11)k = k^{s} + k^{s-1} + ... + k^1 + k^0 = n
        // k^s < n < (k+1)^s
        // k < n^{1/s} < k+1
        
        LL n = stol(N);
        LL ans = n - 1;   // 将答案置为 s=1 的情况
        for (int s = 59; s >= 2; --s) {
            int k = pow(n, 1.0 / s);   // k 为 n^{1/s} 的整数部分
            if (k > 1) {    // 判断 k 是否是一个合法的进制
                LL sum = 1, mul = 1;   // 计算 (11...11)k 对应的十进制值
                for (int i = 1; i <= s; ++i) {
                    mul *= k;
                    sum += mul;
                }
                if (sum == n) {
                    ans = k;
                    break;
                }
            }
        }
        return to_string(ans);
    }
};