动态规划(2.2)背包问题扩展
目錄
一、對體積的限制
(1)體積不大于
(2)體積恰好等于
(3)體積至少為
二、分組背包問題拓展
(1)Acwing 487
(2)背包問題結合樹、dfs(有依賴的背包問題)
三、混合背包問題
四、背包問題求最優方案數
五、背包問題結合貪心
一、對體積的限制
參考文章https://www.acwing.com/file_system/file/content/whole/index/content/1306630/
求方案數初始化?
二維情況:
- 體積至多j,f(0,i)=1,i=0~m,其余是0。
- 體積恰好j,f(0,0)=1,其余是0。
- 體積至少j,f(0,0)=1,其余是0。
一維情況:
- 體積至多j,f(i)=1,i=0~m。
- 體積恰好j,f(0)=1,其余是0.
- 體積至少j,f(0)=1,其余是0
求最大最小值初始化總結
二維情況:
- 體積至多j,f(i,k)=0,i=0~n,k=0~m。(只能求最大值)
- 體積恰好j,
- 求價值最大值:f(0,0)=0,其余-INF
- 求價值最小值:f(0,0)=0,其余INF(只會從0,0與物品的體積和價值,特定的更新某些值)
- ? 體積至少j,f(0,0)=0,其余INF。(只能求最小值)
一維情況:
- 體積至多j,f全為0
- 體積恰好j,
- 求價值最大值,f(0)=0,其余-INF
- 求價值最小值,f(0)=0,其余INF
- 體積至少j,f(0)=0,其余INF
下面展示部分求價值最大值時,體積恰好j,體積至少j的代碼,注意觀察循環體內寫法區別和意義,求體積至多j的問題不再贅述。
01背包體積恰好j
#include <iostream> #include <cstring>using namespace std;const int N = 110, INF = 0x3f3f3f3f;int n, m; int f[N];int main() {cin >> n >> m;memset(f, -INF, sizeof f);f[0] = 0;for(int i = 1;i <= n;i ++){int v, w;cin >> v >> w;for(int j = m;j >= v;j --){f[j] = max(f[j], f[j - v] + w);}}cout << f[m] << endl;return 0; }作者:小呆呆 鏈接:https://www.acwing.com/file_system/file/content/whole/index/content/1306630/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。完全背包恰好j
#include <iostream> #include <cstring>using namespace std;const int N = 110, INF = 0x3f3f3f3f;int n, m; int f[N];int main() {cin >> n >> m;memset(f, -INF, sizeof f);f[0] = 0;for(int i = 1;i <= n;i ++){int v, w;cin >> v >> w;for(int j = v;j <= m;j ++){f[j] = max(f[j], f[j - v] + w);}}cout << f[m] << endl;return 0; }作者:小呆呆 鏈接:https://www.acwing.com/file_system/file/content/whole/index/content/1306630/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。01背包至少j(只能求價值最小值)
不同點:恰好是j問題時,控制了j-v>=0,因為小于0的狀態不存在,不可能由他們更新其它狀態。而至少是j問題時,j-v<0的狀態是合法的,等價于j-v==0,因此j層循環要遍歷0~m,第二維狀態寫法為max(0,j-v)。
#include <iostream> #include <cstring>using namespace std;const int N = 110, INF = 0x3f3f3f3f;int n, m; int f[N];int main() {cin >> n >> m;memset(f, INF, sizeof f);f[0] = 0;for(int i = 1;i <= n;i ++){int v, w;cin >> v >> w;for(int j = m;j >= 0;j --){f[j] = min(f[j], f[max(0, j - v)] + w);//即使物品體積比j大,j - v < 0,也能選,等價于f[0]}}cout << f[m] << endl;return 0; }作者:小呆呆 鏈接:https://www.acwing.com/file_system/file/content/whole/index/content/1306630/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。?完全背包至少j(只能求價值最小值)
#include <iostream> #include <cstring>using namespace std;const int N = 110, INF = 0x3f3f3f3f;int n, m; int f[N];int main() {cin >> n >> m;memset(f, INF, sizeof f);f[0] = 0;for(int i = 1;i <= n;i ++){int v, w;cin >> v >> w;for(int j = 0;j <= m;j ++){f[j] = min(f[j], f[max(0, j - v)] + w);//即使物品體積比j大,j - v < 0,也能選,等價于f[0]}}cout << f[m] << endl;return 0; }二、分組背包問題拓展
(1)Acwing 487
?題目要求:要選擇附件就必需選擇主件。因此我們可以把每一個主件看成一個組,將不選擇、選擇1個附件、兩個附件、、k個附件打包看做背包中的物品,則問題轉化為分組背包問題。每組分為不選擇,選擇主和Cn0個附+Cn1個附+....共2^n種情況。
表示:可以用二進制數來表示打包的方案。例如:1010表示選擇了第2個、第4個附件和主件。
#include<iostream> #include<algorithm> #include<cstring>//每組分為不選擇,選擇主和Cn0個附+Cn1個附+....共2^n種情況,轉化為分組背包問題 using namespace std; const int N =70,M=32010;typedef pair<int,int> PII;PII master[N]; int n,m; int f[M]; vector<PII> servent[N];int main() {cin>>m>>n;for(int i=1;i<=n;i++){int v,w,q;cin>>v>>w>>q;if(!q) master[i]={v,v*w};else servent[q].push_back({v,v*w});}for(int i=1;i<=n;i++){if(master[i].first){for(int j=m;j>=0;j--){auto& sv=servent[i];for(int k=0;k<1<<sv.size();k++){int v=master[i].first,w=master[i].second;for(int u=0;u<sv.size();u++)if(k>>u&1){v+=sv[u].first;w+=sv[u].second;}if(j>=v) f[j]=max(f[j],f[j-v]+w);}}}}cout<<f[m];}作者:yankai 鏈接:https://www.acwing.com/activity/content/code/content/4118462/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。(2)背包問題結合樹、dfs(有依賴的背包問題)
三、混合背包問題
?綜合一下之前學過的,發現狀態轉移方程只和第i層的物品有關,和之前的物品種類無關。因此狀態更新時用對應的轉移方程即可。(這里用了一下代碼簡化版的2進制拆分解多重背包問題)
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N =1010;int f[N]; int n,m; //每種物品根據每種問題的狀態轉移方程做就可以,狀態轉移方程是獨立的,不會因為其他物品的種類變化 int main() {cin>>n>>m;for(int i=1;i<=n;i++){int v,w,s;cin>>v>>w>>s;if(s==0){for(int j=v;j<=m;j++)f[j]=max(f[j],f[j-v]+w);}else{if(s==-1) s=1;for(int k=1;k<=s;k*=2){for(int j=m;j>=k*v;j--)f[j]=max(f[j],f[j-k*v]+k*w);s-=k;}if(s){for(int j=m;j>=s*v;j--)f[j]=max(f[j],f[j-s*v]+s*w);}}}cout<<f[m];return 0; }作者:yankai 鏈接:https://www.acwing.com/activity/content/code/content/4118828/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。四、背包問題求最優方案數
狀態表示為體積恰好是j時,f表示最優方案的取值,g表示最優方案的數量。
f的狀態轉移方程不再贅述,g的狀態轉移方程分為兩部分,不選第i件物品與選擇第i件物品。如果不選是最優解,則g[j]=g[j],如果選第i件物品是最優解,則g[j]=g[j-v],如果兩者相等,則g[j]=g[j]+g[j-v]。
最后遍歷f求得全局最優解的值,然后遍歷g,求得全局最優解方案的數量
#include<iostream> #include<algorithm> #include<cstring> using namespace std;const int N =1010,mod=1e9+7;int f[N],g[N]; int n,m;int main() {cin>>n>>m;//狀態表示為體積恰好是jmemset(f,0xcf,sizeof f);f[0]=0;g[0]=1;for(int i=1;i<=n;i++){int v,w;cin>>v>>w;for(int j=m;j>=v;j--){int cnt=0;int maxv=max(f[j],f[j-v]+w);if(maxv==f[j]) cnt+=g[j];if(maxv==f[j-v]+w) cnt+=g[j-v];g[j]=cnt%mod;f[j]=maxv;}}int res=0;//由于狀態表示是恰好,所以要遍歷求出最優方案所得的值,然后再求最優方案出現的次數for(int i=0;i<=m;i++) res=max(res,f[i]);int cnt=0;for(int i=0;i<=m;i++)if(res==f[i])cnt=(cnt+g[i])%mod;cout<<cnt;return 0; }作者:yankai 鏈接:https://www.acwing.com/activity/content/code/content/4119420/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。五、背包問題結合貪心
?
直覺發現,如果前面吃能量石用過多時間,后面能量就會流失嚴重,因此選擇時應該遵循某種順序選擇。
接下來假設有i,j兩塊能量石,探討一下根據什么順序選擇。
如果按i,j順序吃:
如果按j,i順序吃:
因此選擇順序由s/l決定,s/l越小,后面能量流失就越小。
按s/l排序后,轉化為01背包問題
為了計算損失的能量,狀態表示為體積恰好等于j,狀態轉移方程為:
#include<iostream> #include<algorithm> #include<cstring> using namespace std;const int N =1010,mod=1e9+7;int f[N],g[N]; int n,m;int main() {cin>>n>>m;//狀態表示為體積恰好是jmemset(f,0xcf,sizeof f);f[0]=0;g[0]=1;for(int i=1;i<=n;i++){int v,w;cin>>v>>w;for(int j=m;j>=v;j--){int cnt=0;int maxv=max(f[j],f[j-v]+w);if(maxv==f[j]) cnt+=g[j];if(maxv==f[j-v]+w) cnt+=g[j-v];g[j]=cnt%mod;f[j]=maxv;}}int res=0;//由于狀態表示是恰好,所以要遍歷求出最優方案所得的值,然后再求最優方案出現的次數for(int i=0;i<=m;i++) res=max(res,f[i]);int cnt=0;for(int i=0;i<=m;i++)if(res==f[i])cnt=(cnt+g[i])%mod;cout<<cnt;return 0; }作者:yankai 鏈接:https://www.acwing.com/activity/content/code/content/4119420/ 來源:AcWing 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。?
總結
以上是生活随笔為你收集整理的动态规划(2.2)背包问题扩展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 统计词频 matlab,批量统计若干类词
- 下一篇: 让Windows秒变Mac主题,还原度高