动态规划篇——背包问题

  • 背包问题


  • 0-1 背包

定义

0-1 背包问题的特点是物品只有取与不取两种状态,是最基础的背包问题。

例题

[USACO07DEC] Charm Bracelet S

例题分析

分析

f(i,j) 表示考虑前 i 件物品,最大容量为 j 的背包能包含物品价值的最大值。与此同时,设第 i 件物品的重量与价值分别为 wjvj。考察当前状态:当前状态一定是由前 i1 件物品对应的状态转移而来。对于第 i 件物品,有放与不放两种状态。据此分析,我们可以写出状态转移方程(背包容量不能为负,所以当 j<wi 的时候只能 f(i,j)=f(i1,j)):

f(i,j)=max(f(i1,j),f(i1,jwi)+vi)

因为每一次的状态转移中,我们都只需要比当前物品数量少 1 件的状态,为了减少空间开销,我们可以将状态转移方程压为一维的(此时 f(j) 表示最大容量为 j 的背包能包含的物品的最大值):

f(j)=max(f(j),f(jwi)+vi)

但是,这个时候,对物品与对容量的遍历顺序就需要注意:因为我们能够将二维 DP 压至一维的原理是利用尚未更新的信息。所以,我们应该在外层循环遍历物品,也就是依次更新前 1 件,前 2 件……前 n 件物品的状态,并在内层循环更新对于前 i 件物品,容量不同的状态。注意到在二维的状态转移方程中,我们需要 f(i1,j)f(i1,jwi) 来更新 f(i,j) ,也就是需要尚未更新的 f(j)f(jwi) 来更新 f(j)。所以,我们应该从后往前遍历背包容量,保证 f(j) 一定早于 f(jwi) 更新(因为 j>jwi)。

代码

#include <iostream>
using namespace std;
int dp[13000];
int w[3500], v[3500];
int main()
{
int N, M;
cin >> N >> M;
for (int i = 1; i <= N; i++)cin >> w[i] >> v[i];
for (int i = 1; i <= N; i++)
{
for (int j = M; j >= 1; j--)
{
if (j >= w[i])dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
cout << dp[M] << endl;
return 0;
}

  • 完全背包

定义

完全背包的特点是每个物品可以无限取

例题

疯狂的采药

例题分析

分析

因为每个物品可以无限取,所以状态转移方程应改为:

f(i,j)=max(f(i1,j),f(i,jwi)+vi)

类似地,将其压为一维 DP,状态转移方程即为:

f(j)=max(f(j),f(jwi)+vi)

不同的是,在完全背包问题中,我们需要使用 f(i1,j)f(i,jwi)+vi) 来更新 f(i,j),也就是未更新的 f(j)已经更新的 f(jwi) 来更新 f(j)。所以,我们应该从前往后遍历背包容量,保证 f(j) 一定晚于 f(jwi) 更新(因为 j>jwi)。

代码

#include <iostream>
using namespace std;
long long dp[10000005];
long long w[10005], v[10005];
int main()
{
long long N, M;
cin >> M >> N;
for (long long i = 1; i <= N; i++)cin >> w[i] >> v[i];
for (long long i = 1; i <= N; i++)
{
for (long long j = 1; j <= M; j++)
{
if (j >= w[i])dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
cout << dp[M] << endl;
return 0;
}
posted @   susenyang  阅读(35)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示