動態(tài)規(guī)劃
動態(tài)規(guī)劃就像是解決問題的一種策略,它可以幫助我們更高效地找到問題的解決方案。這個策略的核心思想就是將問題分解為一系列的小問題,并將每個小問題的解保存起來。這樣,當(dāng)我們需要解決原始問題的時候,我們就可以直接利用已經(jīng)計算好的小問題的解,而不需要重復(fù)計算。
動態(tài)規(guī)劃與數(shù)學(xué)歸納法思想上十分相似。
數(shù)學(xué)歸納法:
-
基礎(chǔ)步驟(base case):首先證明命題在最小的基礎(chǔ)情況下成立。通常這是一個較簡單的情況,可以直接驗證命題是否成立。
-
歸納步驟(inductive step):假設(shè)命題在某個情況下成立,然后證明在下一個情況下也成立。這個證明可以通過推理推斷出結(jié)論或使用一些已知的規(guī)律來得到。
通過反復(fù)迭代歸納步驟,我們可以推導(dǎo)出命題在所有情況下成立的結(jié)論。
動態(tài)規(guī)劃:
-
狀態(tài)表示:
-
狀態(tài)轉(zhuǎn)移方程:
-
初始化:
-
填表順序:
-
返回值:
數(shù)學(xué)歸納法的基礎(chǔ)步驟相當(dāng)于動態(tài)規(guī)劃中初始化步驟。
數(shù)學(xué)歸納法的歸納步驟相當(dāng)于動態(tài)規(guī)劃中推導(dǎo)狀態(tài)轉(zhuǎn)移方程。
動態(tài)規(guī)劃的思想和數(shù)學(xué)歸納法思想類似。
在動態(tài)規(guī)劃中,首先得到狀態(tài)在最小的基礎(chǔ)情況下的值,然后通過狀態(tài)轉(zhuǎn)移方程,得到下一個狀態(tài)的值,反復(fù)迭代,最終得到我們期望的狀態(tài)下的值。
接下來我們通過三道例題,深入理解動態(tài)規(guī)劃思想,以及實(shí)現(xiàn)動態(tài)規(guī)劃的具體步驟。
DP41 【模板】01背包
題目解析
第一問
狀態(tài)表示
狀態(tài)表示通常由經(jīng)驗+題目要求得的。
我們最容易想到的狀態(tài)表示是,定義dp[i]表示從前i個物品中挑選,背包所能達(dá)到的最大價值。
動態(tài)規(guī)劃最重要的是希望能夠推導(dǎo)出一個遞推式,使得我們從一個很小的,很容易得到的基礎(chǔ)解,帶入遞推式,反復(fù)推導(dǎo),最后得到我們希望得到的答案。所以我們很容易想到,根據(jù)從前i個物品中挑選,挑選物品的數(shù)量,構(gòu)成我們的每一項,希望能夠推導(dǎo)出遞推式。
如果我們要推導(dǎo)dp[i],我們找不到前面狀態(tài)和i位置狀態(tài)的關(guān)系,我們知道的信息是前面位置狀態(tài)的值,狀態(tài)值表示所能達(dá)到的最大價值,但是我不知道達(dá)到最大價值時,背包的容量,所以我就沒辦法考慮是否選擇第i個物品放入背包,或者不放入背包,如果我們此時還知道對應(yīng)的背包容量,如果還有位置,我們就可以選擇把第i個物品放進(jìn)去,這樣似乎可以推導(dǎo)出狀態(tài)轉(zhuǎn)移方程。所以此時我們還需要表示一個狀態(tài),就是此時背包內(nèi)物品占用的空間是多大。
因此我們可以定義,f[i]表示從前i個物品中挑選,背包所能達(dá)到的最大價值。g[i]表示從從前i個物品中挑選,背包所能達(dá)到的最大價值時,背包占用的體積。
嘗試推導(dǎo)狀態(tài)轉(zhuǎn)移方程,
-
如果把第i個物品放入背包, 此時g[i-1]+v[i]<=V , 此時從前i個物品挑選,所能達(dá)到的最大價值,等價于從前i-1個物品挑選,所能達(dá)到的最大價值加上第i個物品的價值,即在f[i]的基礎(chǔ)上,把第i個物品放入背包,此時f[i]=f[i-1]+w[i],g[i]=g[i-1]+v[i]。
-
如果不把第i個物品放入背包, 此時g[i-1]+v[i]>V, 此時f[i]=f[i-1],g[i]=g[i-1],這樣寫是對的嗎?乍一看好像沒什么問題,但這樣不對,有可能從前i-1個物品中挑選,達(dá)到的價值不是最大,而是第二大,此時的體積再加上第i個位置的物品的體積比背包總體積小,但是兩者價值加起來比f[i-1]大,這種情況f[i]!=f[i-1]。也就是我們還需要遍歷前面所有狀態(tài),看看是不是(g[j]+v[i]<=V)這種情況,如果是,記錄一下f[j]的值,找到最大的f[j]值,然后和f[i-1]比較,選大的值。這樣狀態(tài)轉(zhuǎn)移方程的推導(dǎo)就變得十分麻煩,我們希望改變狀態(tài)表示,使得狀態(tài)轉(zhuǎn)移方程能夠正常推導(dǎo),并且簡便。
改進(jìn),
上面遇到的問題是,我們不知道,從前i-1物品挑選,價值和第二大的體積花費(fèi),價值和第三大的體積花費(fèi)......如果知道這些狀態(tài),我們就可以直接用。
我們需要的狀態(tài)是上面的體積花費(fèi),因此我們可以以體積為劃分,定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。
這樣我們就使得原先只能存儲最大價值的體積g,轉(zhuǎn)變?yōu)榭梢源鎯Σ煌w積花費(fèi)的二維dp。
綜上所述,狀態(tài)表示為,定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。
狀態(tài)轉(zhuǎn)移方程
-
如果把第i個物品放入背包, dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。此時剩下的體積最多是j-v[i]。
-
j-v[i]<0, 此時沒辦法把第i個物品放入背包,此時dp[i][j]=0。
-
j-v[i]=0, 此時把第i個物品放入背包,背包放不下東西了,此時dp[i][j]=w[i]。
-
j-v[i]>0, 此時把第i個物品放入背包,背包還能放j-v[i]體積的物品,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。
-
-
如果不把第i個物品放入背包, 此時背包還能放j體積的物品,從前i-1物品中挑選,dp[i][j]=dp[i-1][j],
當(dāng)j-v[i]=0時,dp[i-1][0]=0,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。
將上述情況合并和簡化,得到狀態(tài)轉(zhuǎn)移方程為,
dp[i][j] = dp[i - 1][j];
if (j >= v[i])
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
初始化
通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界,可能越界的是i-1,而我們狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。所以我們本質(zhì)上多添加了一列。我們只需要初始第一行即可。
i=0表示不選物品,所以價值都為0,第一行全部初始化為0。
填表順序
通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界。
-
固定i改變j, i的變化需要從小到大,j的變化可以從小到大也可以從大到小。
-
固定j改變i, j的變化需要從小到大,因為要用到(i-1,j)位置的狀態(tài),所以i的變化也需要從小到大。
返回值
狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。
結(jié)合題目意思,需要打印dp[n][V]。
這樣我們就解決了第一問。
第二問
狀態(tài)表示
第二問需要求恰好體積為V時,背包所能達(dá)到的最大價值。
在第一問的基礎(chǔ)上,我們很容易定義這樣一個狀態(tài)表示,定義dp[i][j]表示從前i個物品中挑選,總體積恰好為j,所有選法中所能達(dá)到的最大價值。
狀態(tài)轉(zhuǎn)移方程
-
如果把第i個物品放入背包, dp[i][j]表示從前i個物品中挑選,總體積恰好為j,所有選法中所能達(dá)到的最大價值。此時剩下的體積恰好是j-v[i]。
-
j-v[i]<0, 此時沒辦法把第i個物品放入背包,此時dp[i][j]=0。
-
j-v[i]=0, 此時把第i個物品放入背包,背包放不下東西了,此時dp[i][j]=w[i]。
-
j-v[i]>0, 此時把第i個物品放入背包,背包還需要放j-v[i]體積的物品,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。我這樣寫對嗎?此時背包必須放j-v[i]體積的物品,如果前j-1物品挑選不能滿足體積為j-v[i]的情況,此時dp[i][j]=0。如果寫成上面的式子,dp[i][j]=w[i]。因此我們還需要分類討論。
-
如果前j-1物品挑選不能滿足體積為j-v[i]的情況, 此時dp[i][j]=0。
-
如果前j-1物品挑選能滿足體積為j-v[i]的情況, 此時dp[i][j]=d[i-1][j-v[i]]+w[i]。
-
-
-
如果不把第i個物品放入背包, 此時背包還需要放j體積的物品,從前i-1物品中挑選,dp[i][j]=dp[i-1][j]。
當(dāng)j-v[i]=0時,dp[i-1][0]=0,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。
將上述情況進(jìn)行合并和簡化,得到狀態(tài)轉(zhuǎn)移方程,
dp[i][j] = dp[i - 1][j];
if (j >= v[i] && dp[i - 1][j - v[i]] != -1)
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
用-1表示前j-1物品挑選不能滿足體積為j-v[i]的情況。
初始化
通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界,可能越界的是i-1,而我們狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。所以我們本質(zhì)上多添加了一列。我們只需要初始第一行即可。
i=0,表示不選擇物品,最大價值為0,所以(0,0)為0,其他位置為-1,表示達(dá)不到恰好體積為j的情況。
for (int j = 1; j <= V; j++) dp[0][j] = -1;
填表順序
通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界。
-
固定i改變j, i的變化需要從小到大,j的變化可以從小到大也可以從大到小。
-
固定j改變i, j的變化需要從小到大,因為要用到(i-1,j)位置的狀態(tài),所以i的變化也需要從小到大。
返回值
狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。
結(jié)合題目意思,需要打印dp[n][V]。此時需要判斷dp[n][V]是否==-1,等于-1表示不存在,無解返回0,如果不為-1,返回dp[n][V]。
代碼實(shí)現(xiàn)
#include <iostream>
#include <string.h>
using namespace std;
const int N = 1010;
int n, V, v[N], w[N];
int dp[N][N];
int main() {
cin >> n >> V;
for (int i = 1; i <= n; i++)
cin >> v[i] >> w[i];
for (int i = 1; i <= n; i++)
for (int j = 0; j <= V; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= v[i])
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
}
cout << dp[n][V] << endl;
//第二問
memset(dp, 0, sizeof dp);
for (int j = 1; j <= V; j++) dp[0][j] = -1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= V; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= v[i] && dp[i - 1][j - v[i]] != -1)
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
}
cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;
return 0;
}
416. 分割等和子集 - 力扣(LeetCode)
題目解析
狀態(tài)表示
01背包問題本質(zhì)是從一些物品中選物品,本質(zhì)上是有關(guān)組合的問題,而本題目也是在一些數(shù)中挑選出一些數(shù),因為我們可以以01背包為模版,定義狀態(tài)表示。
01背包問題的狀態(tài)表示為,
定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。
定義dp[i][j]表示從前i個物品中挑選,總體積為j,所有選法中,所能達(dá)到的最大價值。
因此我們可以定義dp[i][j]表示能否從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j。
狀態(tài)轉(zhuǎn)移方程
狀態(tài)轉(zhuǎn)移方程的推導(dǎo)是以最后一個位置的狀況進(jìn)行分類討論。
-
如果挑選i位置的元素,
-
如果j-nums[i]<0, 此時表示i位置元素比j還要大,此時dp[i][j]=false;
-
如果j-nums[i] =0, 此時dp[i][j]=true;
-
如果j-nums[i]>0, 此時dp[i][j]=dp[i-1][j-nums[i]];
-
-
如果不挑選i位置的元素, 此時dp[i][j]=dp[i-1][j];
將上述情況進(jìn)行合并和簡化,得到狀態(tài)轉(zhuǎn)移方程,
dp[i][j] = dp[i - 1][j];
if (j > nums[i])
dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i]];
else if(j == nums[i]) dp[i][j] = true;
初始化
根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài)。
所以我們需要初始化第一行位置的狀態(tài),在推導(dǎo)藍(lán)色部分位置狀態(tài)的時候沒有前驅(qū),所以需要初始化這些位置的狀態(tài)值。使用(i-1,j-nums[i])狀態(tài)時,j-nums[i]一定不會越界。所以只需要考慮(i-1,j)
狀態(tài)表示,定義dp[i][j]表示能否從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j。
i==0,表示從只選擇挑選或者不挑選第一個元素,能否使數(shù)字和為j。如果nums[0]==j,或者j==0,此時dp[i][j]=true。
故初始化為,
for (int j = 0; j < n; j++)
if(nums[0] == j || j == 0) dp[0][j] = true;
填表順序
根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài)。使用(i-1,j-nums[i])狀態(tài)時,j-nums[i]一定不會越界。所以只需要考慮(i-1,j)
-
固定i改變j, i的變化,需要從小到大,j的變化可以從小到大也可以從大到小。
-
固定j改變i, j的變化可以從小到大也可以從大到小,i的變化需要從小到大。
返回值
狀態(tài)表示,定義dp[i][j]表示能否從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j。
結(jié)合題目意思,我們需要在nums[0,n-1]區(qū)間挑選元素,使總數(shù)字和為所有元素數(shù)字和的一半。
所以我們可以用aim表示所有元素數(shù)字和的一半,
因此需要返回dp[n-1][aim];
代碼實(shí)現(xiàn)
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size(), sum = 0;
for (auto x : nums)
sum += x;
if (sum % 2)
return false; // 如果不能平分,直接返回 false
int aim = sum / 2;
vector<vector<bool>> dp(n, vector<bool>(aim + 1));
for (int j = 0; j < n; j++)
if(nums[0] == j || j==0) dp[0][j] = true;
for (int i = 1; i < n; i++)
for (int j = 0; j <= aim; j++) {
dp[i][j] = dp[i - 1][j];
if (j > nums[i])
dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i]];
else if (j == nums[i])
dp[i][j] = true;
}
return dp[n - 1][aim];
}
};
494. 目標(biāo)和 - 力扣(LeetCode)
題目解析
狀態(tài)表示
01背包問題本質(zhì)是從一些物品中選物品,本質(zhì)上是有關(guān)組合的問題,而本題目也是在一些數(shù)中挑選出一些數(shù),因為我們可以以01背包為模版,定義狀態(tài)表示。
01背包問題的狀態(tài)表示為,
定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。
定義dp[i][j]表示從前i個物品中挑選,總體積為j,所有選法中,所能達(dá)到的最大價值。
因此我們可以定義dp[i][j]表示從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。
狀態(tài)轉(zhuǎn)移方程
狀態(tài)轉(zhuǎn)移方程的推導(dǎo)是以最后一個位置的狀況進(jìn)行分類討論。
dp[i][j]表示從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。
-
如果挑選i位置的元素,
-
如果j-nums[i]<0, 此時表示i位置元素比j還要大,此時dp[i][j]=0;
-
如果j-nums[i] =0, 此時表示挑選i位置元素后,數(shù)字和就為j了,此時還需要考慮數(shù)字和為0的挑選種類次數(shù),所以此時dp[i][j]=dp[i-1][j-nums[i-1]];
-
如果j-nums[i]>0, dp[i-1][j-nums[i]]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j-nums[i],一共有多少種選法。這些選法的每一種選法中,都加上i位置的元素,選法數(shù)不變,但是這些選法使得數(shù)字和都為j。 此時dp[i][j]=dp[i-1][j-nums[i]];
-
-
如果不挑選i位置的元素, 此時dp[i][j]=dp[i-1][j];
將上述情況進(jìn)行合并和簡化,得到狀態(tài)轉(zhuǎn)移方程,
dp[i][j] = dp[i - 1][j];
if (j >= nums[i])
dp[i][j] += dp[i - 1][j - nums[i]];
初始化
根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài),而只有j>nums[i]的時候才會用到(i-1,j-nums[i])位置的狀態(tài),所以我們只需要考慮(i-1,j)。
所以我們需要初始化第一行位置的狀態(tài),在推導(dǎo)藍(lán)色部分位置狀態(tài)的時候沒有前驅(qū),所以需要初始化這些位置的狀態(tài)值。
我們可以添加虛擬節(jié)點(diǎn),即多添加一行和一列,使這些虛擬節(jié)點(diǎn)成為藍(lán)色位置的前驅(qū),這樣就不用初始化藍(lán)色位置的值,而變?yōu)槌跏蓟摂M節(jié)點(diǎn)即可。
這樣做的好處是,虛擬結(jié)點(diǎn)的初始化可能比藍(lán)色部分位置狀態(tài)的初始化要簡便許多。
添加虛擬結(jié)點(diǎn)后,狀態(tài)表示和狀態(tài)轉(zhuǎn)移方程會發(fā)生改變,即,
dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。
dp[i][j] = dp[i - 1][j];
if (j >= nums[i - 1])
dp[i][j] += dp[i - 1][j - nums[i - 1]];
添加虛擬節(jié)點(diǎn)后,有兩點(diǎn)注意事項,
-
初始化虛擬節(jié)點(diǎn),必須保證推導(dǎo)后續(xù)位置的狀態(tài)的正確性。
-
下標(biāo)的映射關(guān)系。
初始化虛擬節(jié)點(diǎn):
我們根據(jù)狀態(tài)表示,dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。我們同時可以根據(jù)狀態(tài)表示賦予第一行意義,表示不選元素時的情況。
接下來初始化綠色位置的狀態(tài)。
此時表示不選擇元素,此時(0,0)位置dp[0][0]=1,其他位置為0。
下標(biāo)映射關(guān)系:
-
此時,dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。 dp中i對應(yīng)nums的i-1。
-
如果在nums前面添加一個占位符,就可以使得dp中i,j繼續(xù)映射nums1,nums2中i,j。
我們這里選擇第一種解決辦法。
得到初始化,
dp[0][0] = 1;
填表順序
根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài)。使用(i-1,j-nums[i])狀態(tài)時,j-nums[i]一定不會越界。所以只需要考慮(i-1,j)
-
固定i改變j, i的變化,需要從小到大,j的變化可以從小到大也可以從大到小。
-
固定j改變i, j的變化可以從小到大也可以從大到小,i的變化需要從小到大。
返回值
dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。
結(jié)合題目意思,我們需要在nums[0,n-1]區(qū)間挑選元素,使總數(shù)字和為所有元素數(shù)字和的一半。
所以我們可以用aim表示所有元素數(shù)字和的一半,
因此需要返回dp[n][aim];
代碼實(shí)現(xiàn)
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for (auto x : nums)
sum += x;
int aim = (sum + target) / 2;
if (aim < 0 || (sum + target) % 2)
return 0;
int n = nums.size();
vector<vector<int>> dp(n + 1, vector<int>(aim + 1)); // 建表
dp[0][0] = 1; // 初始化
for (int i = 1; i <= n; i++) // 填表
for (int j = 0; j <= aim; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= nums[i - 1])
dp[i][j] += dp[i - 1][j - nums[i - 1]];
}
// 返回結(jié)果
return dp[n][aim];
}
};
結(jié)尾
今天我們學(xué)習(xí)了動態(tài)規(guī)劃的思想,動態(tài)規(guī)劃思想和數(shù)學(xué)歸納法思想有一些類似,動態(tài)規(guī)劃在模擬數(shù)學(xué)歸納法的過程,已知一個最簡單的基礎(chǔ)解,通過得到前項與后項的推導(dǎo)關(guān)系,由這個最簡單的基礎(chǔ)解,我們可以一步一步推導(dǎo)出我們希望得到的那個解,把我們得到的解依次存放在dp數(shù)組中,dp數(shù)組中對應(yīng)的狀態(tài),就像是數(shù)列里面的每一項。最后感謝您閱讀我的文章,對于動態(tài)規(guī)劃系列,我會一直更新,如果您覺得內(nèi)容有幫助,可以點(diǎn)贊加關(guān)注,以快速閱讀最新文章。
最后,感謝您閱讀我的文章,希望這些內(nèi)容能夠?qū)δ兴鶈l(fā)和幫助。如果您有任何問題或想要分享您的觀點(diǎn),請隨時在評論區(qū)留言。
同時,不要忘記訂閱我的博客以獲取更多有趣的內(nèi)容。在未來的文章中,我將繼續(xù)探討這個話題的不同方面,為您呈現(xiàn)更多深度和見解。文章來源:http://www.zghlxwxcb.cn/news/detail-779096.html
謝謝您的支持,期待與您在下一篇文章中再次相遇!文章來源地址http://www.zghlxwxcb.cn/news/detail-779096.html
到了這里,關(guān)于【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!