程式碼隨想錄day 38 || 322 零錢兌換,279 完全平方數,139 單詞拆分

周公瑾55發表於2024-08-23

322 零錢找還

func coinChange(coins []int, amount int) int {
	// 裝滿,並且硬幣無限,可以類比完全揹包問題
	// dp[i][j] 表示前i個物品裝滿容量為j的揹包所需要的最少物品數量
	// 遞推公式 dp[i][j] = min(dp[i-1][j], dp[i][j-w(i)]+1) // 不裝物品i的物品數量,裝物品i的物品數量
	// 初始化 dp[0][0] = 0, 其他數值為math.minint
	// 遍歷順序,本題沒有要求全排列或者全組合,所以用了二維,二維順序是無所謂的,但是習慣還是先物品後背包
	// print
	var dp = make([][]int, len(coins) + 1)
	for idx, _ := range dp {
		dp[idx] = make([]int, amount + 1)
		for j := range dp[idx] {
			dp[idx][j] = amount + 1 // 用來表示一個無法湊成的最大金額,方便在min操作時取到正確的最小值
		}
	}
	dp[0][0] = 0
	for i:=0; i<len(coins); i++{
		for j:=0; j<=amount; j++{
			if j-coins[i] >= 0 { // 要滿足剩餘揹包容量大於等於0
				dp[i+1][j] = min(dp[i][j], dp[i+1][j-coins[i]]+1)
			}else {
				dp[i+1][j] = dp[i][j]
			}
		}
	}

	//fmt.Println(dp)
	if dp[len(coins)][amount] > amount {
		return -1
	}
	return dp[len(coins)][amount]
}

func min(x,y int )int {
	if x < y{
		return x
	}
	return y
}

// 本題思路好找,難點在於取最小值的時候是否能夠想到將dp陣列初始化為一個比較大的數,而不是0
func coinChange(coins []int, amount int) int {
	// 裝滿,並且硬幣無限,可以類比完全揹包問題
	// dp[j] 表示裝滿容量為j的揹包所需要的最少物品數量
	// 遞推公式 dp[j] = min(dp[j], dp[j-w(i)]+1) // 不裝物品i的物品數量,裝物品i的物品數量
	// 初始化 dp[0] = 0, 其他數值為amount + 1, 這裡表示如果全是1來組成amount,需要amount個1,這已經時需要的最大數量,那我設定amount+1,就是要比最大值還要大
	// 遍歷順序,本題沒有要求全排列或者全組合,完全揹包一位陣列按照習慣先正序物品,後正序揹包
	// print
	var dp = make([]int, amount + 1)
	for i, _ := range dp {
		dp[i] = amount + 1
	}
	dp[0] = 0
	for i:=0; i<len(coins); i++{
		for j:=0; j<=amount; j++{
			if j-coins[i] >= 0 { // 要滿足剩餘揹包容量大於等於0
				dp[j] = min(dp[j], dp[j-coins[i]]+1)
			}
		}
	}

	//fmt.Println(dp)
	if dp[amount] > amount {
		return -1
	}
	return dp[amount]
}

func min(x,y int )int {
	if x < y{
		return x
	}
	return y
}

279 完全平方數

func numSquares(n int) int {
	// 完全揹包
	// dp[j] 表示裝滿容量j的揹包需要最少完全平方數
	// 遞推 dp[j] = min(dp[j], dp[j-i^2] + 1)
	// dp[0] = 0  其他初始化成為n+1
	// 先正序物品,後正序揹包 揹包0->n 物品0->n開方+1
	// print

	var dp = make([]int, n+1)
	s := int(math.Sqrt(float64(n))) + 1
	for idx, _ := range dp {
		dp[idx] = n+1
	}
	dp[0] = 0
	for i:=1; i<=s; i++ {
		for j:=i*i; j<=n; j++{
			dp[j] = min(dp[j], dp[j-i*i]+1)
		}
	}
	fmt.Println(dp)
	return dp[n]
}

139 單詞拆分

func wordBreak(s string, wordDict []string) bool {
	// 本題可以聯想到完全揹包問題,但是難點在於,1 dp陣列含義 2 遞推公式 3 遍歷順序
	// dp[j] 表示前j個字母能夠唄字典中的單詞拼出
	// 遞推公式,對於每一個單詞  dp[j] = dp[j-len()] && true  表示本單詞+目標-本單詞長度剩餘的單詞能否被拼出
	// 遍歷順序,字典可能被多次使用,並且順序可以顛倒,所以時全排列,先正序揹包,後正序物品
	// print

	m := minlength(wordDict)
	if len(s) < m {
		return false
	}
	var dp = make([]bool, len(s) + 1)
	dp[0] = true
	for j:=m-1; j<len(s); j++ {
		for i:=0; i<len(wordDict); i++{
			diff := j-len(wordDict[i])+1
			if diff >= 0 && s[diff: j+1] == wordDict[i]{
				dp[j+1] = dp[diff]
			}
			if dp[j+1] == true {
				break
			}
		}
	}

	//fmt.Println(dp)
	return dp[len(s)]
}

func minlength(dict []string) int {
	var m int = math.MaxInt
	for _, v := range dict{
		if len(v) < m {
			m = len(v)
		}
	}
	return m
}

相關文章