题目链接:https://leetcode.com/contest/weekly-contest-116/problems/least-operators-to-express-number/
解题思路:首先,很容易想到将 target m 进制化,然后在 m 进制的表示下求最少的符号表示次数。针对 m 进制下,每个 bit 上的操作存在两种,第一种为 bits[i] * (m^i)
,第二种为 m^(i+1) - (m - bits[i]) * (m^i)
,很容易想到 dp 来求解最优解(当然可以递归,其实递归解这题更简单)。dp[i][0]
表示 i
位在一种操作下需要的最少符号数,dp[i][1]
表示 i
位在第二种操作下需要的最少符号数。注意需要知道当前是否已经存在一个大于0
的数了, 以此来确定在第 i
位进行状态转移时是否进行加 1
操作, 因此我们维护了一个 mark
数组用来保持这个标记. 有些边缘 case 一开始没想到导致代码有点问题,囧。。。
package main
import "fmt"
func calc(idx int) int {
if idx == 0 { // 第0位上的1只能由 x/x 算出
return 1
}
return idx - 1 // 第 i 位可以由 x^i + ... 算出
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
func calcNum(i int, num int, flag bool) int {
if num == 0 {
return 0
}
res := num*calc(i) + (num - 1)
if flag { // 如果前面已经有操作数了, 则在处理第 i 位时需要加上 1
res++
}
return res
}
func leastOpsExpressTarget(x int, target int) int {
bits := []int{}
for target > 0 {
bits = append(bits, target%x)
target /= x
}
bits = append(bits, 0)
dp := [40][2]int{}
mark := [40][2]bool{}
dp[0][0] = calcNum(0, bits[0], false)
dp[0][1] = calcNum(0, x-bits[0], false)
mark[0][0] = bits[0] > 0
mark[0][1] = (x - bits[0]) > 0
for i := 1; i < len(bits); i++ {
v1 := dp[i-1][0] + calcNum(i, bits[i], mark[i-1][0])
v2 := dp[i-1][1] + calcNum(i, bits[i]+1, mark[i-1][1])
dp[i][0] = minInt(v1, v2)
// 这块逻辑可能很绕意思就是当 v1 和 v2 相等时, 如果当前操作数仍然为0, 则我优先标记这个状态
// v1 == v2
// true & true & true
// true & false & true
// true & false & false
// true & true & false
mark[i][0] = true
if dp[i][0] == v1 {
mark[i][0] = mark[i][0] && (mark[i-1][0] || bits[i] > 0)
}
if dp[i][0] == v2 {
mark[i][0] = mark[i][0] && (mark[i-1][1] || bits[i]+1 > 0)
}
v3 := dp[i-1][0] + calcNum(i, x-bits[i], mark[i-1][0])
v4 := dp[i-1][1] + calcNum(i, x-bits[i]-1, mark[i-1][1])
dp[i][1] = minInt(v3, v4)
// 同理
mark[i][1] = true
if dp[i][1] == v1 {
mark[i][1] = mark[i][1] && (mark[i-1][0] || x-bits[i] > 0)
}
if dp[i][1] == v2 {
mark[i][1] = mark[i][1] && (mark[i-1][1] || x-bits[i]-1 > 0)
}
}
return dp[len(bits)-1][0]
}
func main() {
fmt.Println(leastOpsExpressTarget(3, 19))
fmt.Println(leastOpsExpressTarget(5, 501))
fmt.Println(leastOpsExpressTarget(100, 100000000))
fmt.Println(leastOpsExpressTarget(2, 7))
fmt.Println(leastOpsExpressTarget(2, 125046))
fmt.Println(leastOpsExpressTarget(2, 1125082))
}