Least Operators to Express Number

题目链接: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))
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值