揹包dp整理

2022-05-03 10:48:22 字數 4328 閱讀 9673

01揹包

動態規劃是一種高效的演算法。在數學和電腦科學中,是一種將複雜問題的分成多個簡單的小問題思想 ---- 分而治之。因此我們使用動態規劃的時候,原問題必須是重疊的子問題。運用動態規劃設計的演算法比一般樸素演算法高效很多,因為動態規劃不會重複計算已經計算過的子問題。因為動態規劃又可以稱為「記憶化搜尋」。

01揹包是介紹動態規劃最經典的例子,同時也是最簡單的乙個。我們先看看01揹包的是什麼?

問題(01揹包):

有n個重量和價值分別為vi和ci的物品。從這些物品中挑出總重量不超過m的物品,求所有挑選方案中價值總和的最大值。

這就是被稱為01揹包的問題。在沒學習動態規劃之前,我們看到這個問題第一反應會用dfs搜尋一遍。那我們先使用這種方法來求解01揹包問題:

#include#include

using

namespace

std;

const

int n=1e3+1

;int

n,m,v[n],c[n];

//從第i個物品開始挑選總重量不大於sumv的部分

int dfs(int now,int

sumv)

intmain()

/*期望得分:30分

*/

乍一看dfs好像就可以解決這個問題,那還有動態規劃什麼事。然而我們仔細分析一下時間複雜度,每一種狀態都用選或者不選兩種可能。所以我們可以得出使用dfs的時間複雜度為o(2^n)。顯然這個方法不是乙個很好的方法,因為這個時間複雜度太高了。我們仔細研究可以發現,造成時間複雜度這麼高的原因是重複計算。既然我們找到複雜度這麼高的原因,那我們就可以想辦法減少它重複計算的次數。仔細分析容易想到,使用乙個二維陣列來記錄每一次搜尋的答案,這樣我們就避免了重複計算。

這樣的小技巧,我們稱之為記憶化搜尋。我們只是小小的改變就讓它的時間複雜度降低至o(nw)。

#include#include

#include

using

namespace

std;

const

int n=1e3+1

;int n,m,v[n],c[n],dp[n][n*10];//

儲存每一次搜尋的答案

int dfs(int now,int

sumv)

intmain()

/*期望得分:60-80分

*/

仔細分析,可以發現我們還可以有更簡單的寫法(轉成遞推):

#include#include

#include

using

namespace

std;

const

int n=1e3+1

;int n,m,v[n],c[n],dp[n][n*10

];//

dp[i][j] 表示取了i個物品挑選出總重量不超過j的物品時,揹包中的最大價值

intmain()

}printf("%d

",dp[1

][m]);

return0;

}/*期望得分:60-80分

*/

然後dp[i][j]的僅由dp[i+1][j]||dp[i+1][j-v[i]]轉移而來,於是我們可以滾動第一維:

#include#include

#include

using

namespace

std;

const

int n=1e3+1

;int n,m,v[n],c[n],dp[2][n*10

];int

main()

}printf("%d

",dp[now][m]);

return0;

}/*期望得分:100分

*/

凡是可以滾動的,必定可以降維。——shenben

#include#include

#include

using

namespace

std;

const

int n=1e3+1

;int n,m,v[n],c[n],dp[n*10

];int

main()

}printf("%d

",dp[m]);

return0;

}/*期望得分:100分

*/

使用遞推方程直接求解的方法,我們稱之為dp。因為他每一次的選取,都在動態的計算最優的情況。當然可能他區域性不是最優,但是整體一定是最優解。這就是他和貪心演算法最大的不同,貪心演算法,每一次都是最優,但是整體不一定不是最優。

多重揹包

問題(多重揹包):
就是乙個0,1,2……k揹包(往01揹包上想就好了)

有n種重量和價值分別為vi和ci的物品,每種物品有si個。從這些物品中挑出總重量不超過m的物品,求所有挑選方案中價值總和的最大值

搜尋

//

期望得分:30-70分

#include#include

#define n 10100

using

namespace

std;

intv[n],c[n],s[n],n,m;

intans;

void dfs(int now,int sumv,int

sumc)

dfs(now+1

,sumv,sumc);

for(int i=1;i<=s[now];i++) dfs(now+1,sumv+i*v[now],sumc+i*c[now]);

}int

main()

記憶化搜尋

//

期望得分:60-100分

#include#include

#define n 10100

using

namespace

std;

intv[n],c[n],s[n],n,m;

intans;

int dp[101

][n];

int dfs(int now,int

sumv)

return

dp[now][sumv];

}int

main()

記憶化搜尋轉遞推

//

期望得分:60-100分

#include#include

#define n 10100

using

namespace

std;

intv[n],c[n],s[n],n,m;

intans;

int dp[101

][n];

intmain()}}

printf("%d

",dp[1

][m]);

return0;

}

滾動陣列(滾動第一維)

//

期望得分:100分

#include#include

#define n 10100

using

namespace

std;

intv[n],c[n],s[n],n,m;

intans;

int dp[2

][n];

intmain()}}

printf("%d

",dp[now][m]);

return0;

}

降維(用二進位制優化)

//

期望得分:100分

#include#include

using

namespace

std;

inline

intread()

while(ch>='

0'&&ch<='9')

return x*f;

}const

int n=1e5+10

;int n,m,cnt,xp[30

];int f[n*200

],v[n],c[n];

intmain()

if(z>0

) }

for(int i=1;i<=cnt;i++)

}printf("%d

",f[m]);

return0;

}

其他揹包dp自行整理

揹包 DP 揹包

揹包 題目 是dp中較為常見的題目 分為 0 1 揹包 完全揹包 和多重揹包 這三類 是越來越深入的首先來介紹一下 0 1揹包 首先 0 1 揹包的含義是 給你乙個容量位m的揹包 然後給你n個物品 每個物品具有一定價值和一定重量 會站一定的揹包空間 答案是在n個物品中那幾個 然後使得到的價值最大 首...

揹包dp之01揹包

現在我們有n個配件,他們有不同的價值.但是我們揹包的容量是有限的,因為我們只有乙個一級包,所以我們最多可以裝v重量的東西.但是為了能更好的吃到雞 不存在的 我們要攜帶更有價值的配件,請問我們最多能拿多少價值的配件來當快遞員呢?輸入的第一行是t,表示有一共要打t場比賽.每組資料由三行組成.第一行包含兩...

dp 揹包之多重揹包

問題 多重揹包也是 0 1 揹包的乙個變式。與 0 1 揹包的區別在於每種物品有ki個,而非乙個。解決方案 將k個相同的物品,看作k個不同的物品,但是wi,ci都一樣。即可套用 01揹包方案 詳見 優化方法 二進位制優化 設k個物品分成 a xx a xx 1 a xx k 1 個物品。那麼 a x...