当前位置: 首页>开发笔记>正文

01背包 完全背包

01背包 完全背包

Powered by:AB_IN 局外人

我太菜了,现在才学

一、01背包

看的入门班的录播,大约明白了。

n n n是物品的个数, c c c是背包的容量, w [ i ] w[i] w[i]代表第 i i i物品的重量, v [ i ] v[i] v[i]代表第 i i i物品的价值。

先给出最原始推出来的式子

dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);

什么意思呢?

d p [ i ] [ j ] dp[i][j] dp[i][j]表示 i i i件物品恰好放入一个容量为 j j j的背包里可以获得的最大价值。

  • d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]就表示,不选第 i i i个物品,就让前 i − 1 i-1 i1个物品去组成容量为 j j j的背包。
  • d p [ i − 1 ] [ j − w [ i ] ] dp[i-1][j-w[i]] dp[i1][jw[i]]就表示,选第 i i i个物品,让 j − w [ i ] j-w[i] jw[i]的背包正好加上第 i i i个物品,容量不就正好为 j j j了嘛。
  • 这两个取最大值即可。

但问题是这样太浪费空间了,可以通过式子看出 d p [ i ] [ ] dp[i][] dp[i][]只和 d p [ i − 1 ] [ ] dp[i-1][] dp[i1][]有关,也就是和上一行。再加上 i i i是从 1 1 1 n n n的,也就是前面一些行就没有用了,会很浪费空间。

所以便有了01滚动就地滚动

  • 01滚动。顾名思义,就是两行之间的相互利用。 d p [ 0 / 1 ] [ j ] dp[0/1][j] dp[0/1][j]一行记录前一行的值,另一行记录当前行的值。
  • 就地滚动。顾名思义,就是用一个一维数组,之前的状态和当前的状态都在同一个数组里。

显然就地滚动更佳,因为只用一维数组。

但如果 j j j w [ i ] w[i] w[i] c c c的话,会出现 b u g bug bug,一个物品会被算多次,因为 j j j会一直往右走到 c c c。比如一个放进去了, j j j往右走,会走到刚刚放进去的那个状态,原先放进去的会被再放一遍,显然这样是错的,因为上一行状态和这一行状态混了。

那该怎么办呢?

j j j从右往左走即可。上一行的状态在 j j j的左边,这一行的状态在 j j j的右边。

代码孕育而生。

for(int i=1;i<=n;i++){for(int j=c;j>=w[i];j--){dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}
}

爱玩游戏的Tom

#include<bits/stdc++.h>
using namespace std;
const int N=50005;
typedef long long ll;
ll n,m,dp[N],c[N],w[N];
int main()
{cin>>n>>m;for(ll i=1;i<=n;i++){cin>>c[i]>>w[i];}for(ll i=1;i<=n;i++){for(ll j=m;j>=c[i];j--){dp[j]=max(dp[j],dp[j-c[i]]+w[i]);}}cout<<dp[m]<<endl;return 0;
}

小梁的背包

输出背包物品的数量背包物品的价值总和

#include<bits/stdc++.h>
using namespace std;#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define ull unsigned long long
#define rint register int
#define ld long double
#define db double
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define rep1(i,a,n) for (rint i=a;i<n;i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define per1(i,a,n) for (rint i=a;i>n;i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define sd(x) scanf("%d",&(x))
#define slld(x) scanf("%lld",&(x))
#define sdd(x,y) scanf("%d%d",&(x),&(y))
#define sc(s) scanf("%s",(s))
#define pd(x) printf("%d\n",(x))
#define plld(x) printf("%lld\n",(x))
#define pdk(x) printf("%d ",(x))
const int inf=0x3f3f3f3f;namespace IO{char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);inline char gc(){if(ip!=ip_)return *ip++;ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);return ip==ip_?EOF:*ip++;}inline void pc(char c){if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;*op++=c;}inline ll read(){register ll x=0,ch=gc(),w=1;for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;return w*x;}template<class I>inline void write(I x){if(x<0)pc('-'),x=-x;if(x>9)write(x/10);pc(x%10+'0');}class flusher_{public:~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}}IO_flusher;
}
using namespace IO;
const int N=1e4+10;
ll t,n,s,w[N],v[N],dp[N],num[N];
int main()
{t=read();while(t--){n=read();s=read();mset(dp, 0);mset(num, 0);rep(i, 1, n) v[i]=read(),w[i]=read();rep(i, 1, n){per(j, s, v[i]){//判断数量在前!if(dp[j-v[i]]+w[i]>dp[j]) {num[j]=num[j-v[i]]+1;dp[j]=dp[j-v[i]]+w[i];}}}write(dp[s]);pc(' ');write(num[s]);pc('\n');}return 0;
}

二、完全背包

n n n是物品的个数, v v v是背包的容量,每种物品都有无限件可用 c [ i ] c[i] c[i]代表第 i i i物品的费用, w [ i ] w[i] w[i]代表第 i i i物品的价值。

在讲就地滚动的时候,如果 j j j c [ i ] c[i] c[i] v v v的话,会出现 b u g bug bug,一个物品会被算多次,这不恰好是我们想要的吗?

所以

for(int i=1;i<=n;i++){for(int j=c[i];j<=v;j++){dp[j]=max(dp[j],dp[j-c[i]]+w[i]);}
}

Optimal Coin Change

这个题不能按照上面的板子直接套。

首先先看题面,是让我们把 v v v拆成题目给的尽可能少的硬币,如果可能的解决方案不止一种,则应输出使用更多面值较低的硬币的解决方案 。

这里的价值 w w w就是1,而这里我们要的是装满背包最少的价值,所以在 d p dp dp数组的定义上,还有转移方程的判定条件上会有改动。

  • d p dp dp数组的定义上,都变成 i n f inf inf d p [ 0 ] = 0 dp[0]=0 dp[0]=0

  • 核心代码

    if (dp[j - f[i]] < inf && dp[j - f[i]] + 1 <= dp[j]) {dp[j] = dp[j - f[i]] + 1;pre[j] = j - f[i];
    }
    

    如果这里用大于号的话,那么一直取最小的硬币即可,因为每个硬币的 w w w都为1。所以这里不应该用大于号,应该用 ≤ \le

    那为什么用 = = =呢?因为如果要数量少,还得往后递推,用较大面值补上一些多余的小面值钱数。

    另外就是背包路径记录。用链表记录即可。用 j j j指向上一个 j − f [ i ] j-f[i] jf[i]

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e6+10;
int v, n, f[12], dp[N], pre[N], a[N];
int main() {while (~scanf("%d%d", &v, &n)) {memset(dp, 0x3f, sizeof(dp));memset(a, 0, sizeof(a));for (int i = 1; i <= n; i++)scanf("%d", &f[i]);dp[0] = 0;for (int i = 1; i <= n; i++) {for (int j = f[i]; j <= v; j++) {if (dp[j - f[i]] < inf && dp[j - f[i]] + 1 <= dp[j]) {dp[j] = dp[j - f[i]] + 1;pre[j] = j - f[i];}}}if (dp[v] == inf)puts("-1");else {while (v) {a[v - pre[v]]++;v = pre[v];}for(int i=1;i<=n;i++){printf("%d ",a[f[i]]);}printf("\n");}}return 0;
}
/*
2000 7 10 20 50 100 200 500 1000
250 4 10 20 125 150
35 4 10 20 125 150
48 4 1 8 16 20
40 4 1 10 13 37
43 5 1 2 21 40 80
*/

完结。

https://www.zydui.com/af0f5V28GDQM.html
>

相关文章:

  • 01背包为什么不能用贪心
  • gself完美背包
  • 01背包问题可以用哪些方法
  • 单肩包30h0g1sl1
  • 迷你双肩包小背包
  • 如何讲解完全背包
  • 2018新款小背包
  • 110l背包图片
  • vscode搭建nodejs環境,關于VS code ESP-IDF 提示“loading ‘build.ninja‘: 系統找不到指定的文件” 的解決方案
  • 什么是應用軟件并舉例,16.應用舉例
  • 【面經】美團春招三輪面經分享~涵蓋眾多知識點
  • 2021年面試題目,面試題--新增
  • magic king怎么讀,magick++ 簡介
  • 微信怎么設置定時發送,朋友圈可以定時發送嗎?
  • can not connect to rpc service,RPC service
  • ftpserver安卓版,FTPServer
  • server u使用教程,Server-U
  • rpc服務器,RPC 和 Web Service 有什么區別?
  • rpc服務器,web service和rpc的區別
  • psexec
  • dhclient命令,hpe?3par命令行查看狀況腳本
  • hp存儲默認管理口地址,HP3par 多路徑存儲磁盤使用方法
  • hp3par命令行手冊,3par命令集
  • 存儲器芯片的地址范圍,存儲器芯片類別有哪些?
  • 在pc機中各類存儲器,1.14各類存儲器芯片
  • 存儲芯片漲價最新消息,存儲器芯片
  • Windows/Linux性能監控軟件>csv文件,方便生成圖表
  • sqlserver nvarchar,【SQL開發實戰技巧】系列(四十五):Oracle12C常用新特性?VARCHAR2/NVARCHAR2類型最大長度由40
  • arcgis怎么導入地圖,Arcgis路網導入3dmax批量改成道路面
  • 定義animal父類,定義一個父類Animal eat方法 , 定義兩個子類 Dog 特有方法keepHome , Cat 特有方法 catchMouse ;并
  • 手機連接兩個藍牙方法,打開藍牙的設置
  • iconfont圖標免費嗎,關于阿里矢量圖標彩色icon使用
  • ps制作賽博朋克風格,如何用ps做出賽博朋克的風格?
  • ue4綠幕實時導入場景,如何在UE4中制作賽博朋克LED效果
  • 產品經理有哪些培訓課程,2023年全國NPDP產品經理國際認證火熱招生啦
  • B端產品需要什么能力,NPDP認證|B端產品經理是如何做競品調研的?
  • 超級工具,Supershell 一款牛叉閃閃的工具
  • buffer在c語言中是什么意思,QBuffer 用法理解