动态规划----0 1 背包

动态规划----0 / 1 背包

我一直觉得我对背包问题其实是会的,知道今天看到一道背包题发现两眼迷茫…

所以写一写关于背包问题的总结,复习一下

首先是最基础的 0 / 1背包问题

上题目:

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 2 2 2 个整数 T T T 1 ≤ T ≤ 1000 1 \le T \le 1000 1T1000)和 M M M 1 ≤ M ≤ 100 1 \le M \le 100 1M100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。

接下来的 M M M 行每行包括两个在 1 1 1 100 100 100 之间(包括 1 1 1 100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

样例 #1

样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

3

提示

【数据范围】

  • 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M10
  • 对于全部的数据, M ≤ 100 M \le 100 M100

【题目来源】

NOIP 2005 普及组第三题

所谓0/1背包就是对于一个物体,要么取,要么不去,就对应着二进制中的 0 和 1

考虑问题所涉及的变量,分别有这几个可变因素

1.我总共的时间

2.某个物品所需要的时间

3.某个物品所拥有的价值

我们直接考虑中间过程,对于第 i 个物品 , 假设他的价值为 vi,,所需要的时间为 t i他有两个选择

1.拿:

那么此时他能获得的价值是背包容量减去i 自身所需容量情况下的最优解加上i的价值

2.不拿

那么此时能获得的最大价值为当前背包容量所能获得的最大值

又因为背包本身随着物品的拿或者不拿在动态的变化着,所以dp方程中有一个维度要给背包的容量

而物品是在不断选择的,所以有一个维度要来判断选择的物品编号

所以 dp[i] [j] 代表着在背包容量为i的情况下1~j个物品所可以达到的最大值

那么转移方程可以写为

dp[ i ] [ j ] = max(dp[ i - weight[ j ] ] [ j - 1 ] + value[ j ] , dp[ i ] [ j - 1])

(weight[ j ] , value[ j ]分别为物品 j 所需的容量以及价值)

同时

背包的容量可以为0 , 所选的物品也可以为0

所以边界条件为dp[0] [ ] = dp[ ] [0] = 0;

所以代码为:

#include
using namespace std;
int main()
{
    int total_time , total_number;
    cin >> total_time >>total_number;
    int time[total_number]={0} , value[total_number] = {0};
    for(int temp = 0 ; temp < total_number ; temp++)
        cin >> time[temp] >> value[temp];
    int dp[total_time + 1][total_number + 1] ;
    for(int temp = 0 ; temp < total_number+1 ; temp++) //给两个边界先提前赋值为0
        dp[0][temp] = 0;
    for(int temp = 0 ; temp < total_time+1 ; temp++)
        dp[temp][0] = 0;
    for(int temp1 = 1 ; temp1 < total_time + 1 ; temp1++){
        for(int temp2 = 1 ; temp2 < total_number + 1 ; temp2++){
            if(temp1 < time[temp2 - 1])
                dp[temp1][temp2] = dp[temp1][temp2 - 1];
            else
                dp[temp1][temp2] = max(dp[temp1 - time[temp2 - 1]][temp2 - 1] + value[temp2 - 1] , dp[temp1][temp2 - 1]);
        }
    }
    cout << dp[total_time][total_number];
    return 0;
}

当然如果条件给个数子太大,开二维数组可能会RE,所以我们可以进行优化

由上可知:

dp[ i ] [ j ] = max(dp[ i - weight[ j ] ] [ j - 1 ] + value[ j ] , dp[ i ] [ j - 1])

我们将两个维度互换一下:

dp[ j ] [ i ] = max( dp[ j - 1] [ i - wight[ i ]] + value [ j ] , dp [ j - 1] [ i ])

可以观察到第一个维度只有i - 1 了,i - 1之前的数据都无效了,所以我们可以将它优化为一维数组

即:

dp[ i ] = max(dp[ i - weight [ i ]] + value[j] , dp[i - 1])

有一个重要的点是

必须要从后往前遍历,应为你要使用的值应该是之前的值,如果从前遍历的话,值会更新

#include
using namespace std;
int main()
{
    int total_time , total_number;
    cin >> total_time >> total_number;
    int time[total_number] = {0} , value[total_number] = {0};
    for(int temp = 0 ; temp < total_number ; temp++)
        cin >>time[temp] >> value[temp];
    int dp[total_time + 1] = {0};
    for(int temp1 = 0 ; temp1 < total_number ; temp1++){
        for(int temp2 = total_time ; temp2 >= 0 ; temp2--){
            if(temp2 < time[temp1])
                continue;
            else
                dp[temp2] = max(dp[temp2 - time[temp1]] + value[temp1] , dp[temp2]);
        }
    }
    cout << dp[total_time];
    return 0;
}

你可能感兴趣的:(动态规划)