背包问题(01)
\(01\)背包问题
我学两天了两天的动态规划终于学会了01背包问题,特来记录一下算法过程。
算法主题思路
先来看一个很经典的题目:
每种物品都有一个价值 \(w\) 和体积 \(c\) ,你现在有一个背包容积为V,你想用一些物品装背包使得物品总价值最大。
看着这个题目,就是一个很经典的背包问题 ,然后来想一想\(\color{blue}{动态转移方程}\)是什么,通过简单地在纸上画画就可以知道子问题为“ \(f[i][v]\) 表示前 \(i\) 件物品恰放入一个容量为 \(v\) 的背包可以获得的最大价值。”
然后\(\color{blue}{动态转移方程}\) 可以得到是: $$ f(i,v) = max( f(i-1,v) , f(i-1,v-w[i])+ c[i]); $$ 。
这个方程的意思是将前i件物品放进v-w[i]容量的包里的价值与不取这个物品的价值取最大值。
缺点:
用了 2 维数组,当数据大一些时容易 \(MLE\) ,所以我们来想办法来优化算法。
优化:
既然 2 维数组不怎么好,那我们要就把 2 维 \(\Rightarrow\) 1 维 。
先上\(\color{blue}{动态转移方程}\):
$ f(v) = max(f(v),f(v-w[i])+c[i]) $
由于只需要最后f[v]的值,倒推前一个物品,其实只要知道f[v-w[n]]即可。以此类推,对以第j个背包,其实只需要知道到f[v-sum{w[j..n]}]即可,即代码中的
for i=1..N
for v=V..0
你品,你细品
然后为了大家好好食用,来一道题目来看看。
P1048 [NOIP2005 普及组] 采药
这道题,我直接放代码吧:
#include<cstdio>
#include<iostream>
using namespace std;
int f[100000];
int cost[3410],waste[3410];
int main()
{
int n,m;
cin>>m>>n;
for(int i=1;i<=n;i++)
scanf("%d %d",&waste[i],&cost[i]);
for(int i=1;i<=n;i++) // 可以背掉这三行
for(int v=m;v>=waste[i];v--)
f[v] = max(f[v],f[v-waste[i]]+cost[i]); // 动态转移方程
cout<<f[m]<<endl;
return 0;
}