背包问题(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;
}
posted @ 2021-03-17 16:45  在那遥远的悠穹下  阅读(64)  评论(0编辑  收藏  举报