01背包问题
01背包问题对于动态规划dp的入初学者非常友好,题目和分析简单易懂,很适合上手,先来看一下题目的描述:
描述
一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W_1,W_2,...,W_n,它们的价值分别为C_1,C_2,...,C_n,求旅行者能获得最大总价值。
输入描述
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);
第2..N+1行:每行二个整数W_i,C_i,表示每个物品的重量和价值。
输出描述
仅一行,一个数,表示最大总价值。
用例输入 1
10 5
2 6
2 3
6 5
5 4
4 6
用例输出 1
15
如果没有接触过动规,拿到这个题目可能会一时没有思绪,就像本人一样
但是没有关系,我们接下来通过一个表格来分析
其中横向表示bag的容量(从左向右逐渐递增)
竖向表示每组数据(质量,价值)
现在来解释一下这个表格:
eg.第一组数据,当背包容量为0和1时装不下,所以在dp[ ] [ ]中填0;当背包容量>=2时,可以放一个物品,并且仅仅可放一个(因为物件只有一个),所以后面填的都是6
第二组数据(2行)同上,但当背包容量=2时,可以尝试放入物件2,
怎么放呢?
我们可以把原来的一号物件拿出来,然后再放进去;即现在dp[2][2]=dp[1][0]+value=3;
但是,根据贪心算法,我们要的是价值最大的,而3比原来装1物件时的6小,所以任然保留6
可以发现,当物件n的质量为W时,我们就回去找dp[n-1][此时的背包容量-W]的值并加上该物件的价值,将它与dp[n-1][W]比较,取最大者;
表格中的横线也正是这个意义
现在我们就可以得出递推公式:V是当前背包容量
公式既然出来了,那就直接实现代码吧
代码不难,主要是理解思想
#include<bits/stdc++.h>
using namespace std;
int dp[205][205];
struct mv{
int m;
int v;
};
mv mine[35];//定义结构体比较直观
int main()
{
int bag,n;
cin>>bag>>n;
for(int i=1;i<=n;i++)
cin>>mine[i].m>>mine[i].v;
memset(dp,0,sizeof(dp));//刷0
for(int i=1;i<=n;i++)
for(int j=1;j<=bag;j++)
{
if(j<mine[i].m)//当前的背包容量装不下该物件
{
dp[i][j]=dp[i-1][j];
}
else
{
dp[i][j]=max(dp[i-1][j-mine[i].m] + mine[i].v , dp[i-1][j]);//装得下就套公式
}
}
cout<<dp[n][bag];//输出最右下角的数就是结果
return 0;
}
这个式子和递推很像,每次操作都是局部最优解,最后堆叠起来的就是全局最优解
dp的例题还有最长子序列问题,可以等等看看