leedcode 算法刷题第三十三天
322. 零钱兑换
class Solution {
public:int coinChange(vector<int>& coins, int amount) {vector<int> dp(amount+1,INT_MAX);dp[0] = 0;for(int i=1;i<=amount;i++){for(int j =0;j<coins.size();j++){if(i>=coins[j]&&dp[i-coins[j]]!=INT_MAX){dp[i] = min(dp[i-coins[j]]+1,dp[i]);}}}if(dp[amount]==INT_MAX)return -1;return dp[amount];}
};
定义一维dp设置初始值维INT_MAX dp[0] = 0;循环背包和物品,当背包大于物品,并且前面保存的元素有效时,保存当前结果。
279. 完全平方数
class Solution {
public:int numSquares(int n) {vector<int> dp(n+1,INT_MAX);dp[0] =0;for(int i = 0;i<=n;i++){for(int j=1;j*j<=i;j++){if(j*j<=i&&dp[i-j*j]!=INT_MAX){dp[i] = min(dp[i],dp[i-j*j]+1);}}}if(dp[n]==INT_MAX)return -1;return dp[n];}
};
换汤不换药。
139. 单词拆分
class Solution {
public:bool wordBreak(string s, vector<string>& wordDict) {vector<bool> dp(s.size()+1,false);unordered_set wordSet(wordDict.begin(),wordDict.end());dp[0] = true;for(int i=1;i<=s.size();i++){for(int j=0;j<i;j++){string word = s.substr(j, i - j);if(wordSet.find(word)!=wordSet.end()&&dp[j]==true){dp[i] = true;}}}return dp[s.size()];}
};
1. 问题定义
判断字符串 s
是否能被拆分成字典 wordDict
中的一个或多个单词。
2. 状态定义
dp[i]
:表示字符串 s
的前 i 个字符(即 s[0]
到 s[i-1]
)能否被成功分割。
3. 基础情况
dp[0] = true
:空字符串总是可以被分割(这是动态规划的起点)
4. 状态转移思路
对于每个位置 i
(从 1 到 n),我们检查所有可能的分割点 j
(从 0 到 i-1):
-
如果
dp[j] == true
(前 j 个字符可以分割) -
并且 子串
s[j]
到s[i-1]
在字典中 -
那么
dp[i] = true
(前 i 个字符也可以分割)
5. 直观示例
假设 s = "leetcode"
, wordDict = ["leet", "code"]
text
i=0: dp[0] = true (空字符串) i=1: 检查 "l" → 不在字典中 → dp[1]=false i=2: 检查 "le" → 不在字典中 → dp[2]=false i=3: 检查 "lee" → 不在字典中 → dp[3]=false i=4: 检查 "leet" → 在字典中,且dp[0]=true → dp[4]=true ... i=8: 检查 "code" → 在字典中,且dp[4]=true → dp[8]=true
6. 算法流程
text
初始化 dp[0] = true 对于 i 从 1 到 n:对于 j 从 0 到 i-1:如果 dp[j] 为真 且 s[j:i] 在字典中:设置 dp[i] = true跳出内层循环(优化) 返回 dp[n]