结合 (2)(3) 两式可知,当 m>1 时,有 k^m < n < (k+1)^m。两边同时开方得:
k < \sqrt[m]{n} < k+1
依据这个公式我们知道,\sqrt[m]{n 必然为一个小数,且 k 为 \sqrt[m]{n 的整数部分,即 k = \lfloor \sqrt[m]{n} \rfloor。
这个结论帮助我们在 n 和 m 已知的情况下快速确定 k 的值。
综合上述两个结论,依据结论一,我们知道 m 的取值范围为 [1,\log_k n),且 m = 1 时必然有解。因为随着 m 的增大,k 不断减小,所以我们只需要从大到小检查每一个 m 可能的取值,利用结论二快速算出对应的 k 值,然后校验计算出的 k 值是否有效即可。如果 k 值有效,我们即可返回结果。
在实际代码中,我们首先算出 m 取值的上界 mMax,然后从上界开始向下枚举 m 值,如果当前 m 值对应的 k 合法,那么我们即可返回当前的 k 值。如果我们一直检查到 m=2 都没能找到答案,那么此时即可直接返回 m=1 对应的 k 值:n-1。
代码
[sol1-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution { public: string smallestGoodBase(string n){ long nVal = stol(n); int mMax = floor(log(nVal) / log(2)); for (int m = mMax; m > 1; m--) { int k = pow(nVal, 1.0 / m); long mul = 1, sum = 1; for (int i = 0; i < m; i++) { mul *= k; sum += mul; } if (sum == nVal) { returnto_string(k); } } returnto_string(nVal - 1); } };
[sol1-Java]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public String smallestGoodBase(String n) { longnVal= Long.parseLong(n); intmMax= (int) Math.floor(Math.log(nVal) / Math.log(2)); for (intm= mMax; m > 1; m--) { intk= (int) Math.pow(nVal, 1.0 / m); longmul=1, sum = 1; for (inti=0; i < m; i++) { mul *= k; sum += mul; } if (sum == nVal) { return Integer.toString(k); } } return Long.toString(nVal - 1); } }
[sol1-C#]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
publicclassSolution { publicstringSmallestGoodBase(string n) { long nVal = long.Parse(n); int mMax = (int) Math.Floor(Math.Log(nVal) / Math.Log(2)); for (int m = mMax; m > 1; m--) { int k = (int) Math.Pow(nVal, 1.0 / m); long mul = 1, sum = 1; for (int i = 0; i < m; i++) { mul *= k; sum += mul; } if (sum == nVal) { return k.ToString(); } } return (nVal - 1).ToString(); } }
[sol1-JavaScript]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
var smallestGoodBase = function(n) { const nVal = parseInt(n); const mMax = Math.floor(Math.log(nVal) / Math.log(2)); for (let m = mMax; m > 1; m--) { const k = BigInt(Math.floor(Math.pow(nVal, 1.0 / m))); if (k > 1) { let mul = BigInt(1), sum = BigInt(1); for (let i = 1; i <= m; i++) { mul *= k; sum += mul; } if (sum === BigInt(n)) { return k + ''; } } } return (BigInt(n) - BigInt(1)) + ''; };
[sol1-Golang]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
funcsmallestGoodBase(n string)string { nVal, _ := strconv.Atoi(n) mMax := bits.Len(uint(nVal)) - 1 for m := mMax; m > 1; m-- { k := int(math.Pow(float64(nVal), 1/float64(m))) mul, sum := 1, 1 for i := 0; i < m; i++ { mul *= k sum += mul } if sum == nVal { return strconv.Itoa(k) } } return strconv.Itoa(nVal - 1) }
[sol1-C]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
char* smallestGoodBase(char* n) { long nVal = atol(n); int mMax = floor(log(nVal) / log(2)); char* ret = malloc(sizeof(char) * (mMax + 1)); for (int m = mMax; m > 1; m--) { int k = pow(nVal, 1.0 / m); long mul = 1, sum = 1; for (int i = 0; i < m; i++) { mul *= k; sum += mul; } if (sum == nVal) { sprintf(ret, "%lld", k); return ret; } } sprintf(ret, "%lld", nVal - 1); return ret; }