一、概述
动态规划可能是信息学中比较棘手的一块内容了,个人认为本质和搜索比较相似。
背包问题01:有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
如果我们定义一个二维数组dp[i][j]
,dp[ i ][ j ] 表示 在面对第 i 件物品,且背包容量为 j 时所能获得的最大价值 ,那么我们会发现,dp[0][j]
和dp[i][0]
都要确保为0
。
那么对于第i
行怎么填写呢?不难发现对于一个物品,只有两种情况:
情况一: 第i件不放进去,这时所得价值为:dp[i-1][v]
情况二: 第i件放进去,这时所得价值为:dp[i-1][v-c[i]]+w[i]
状态转移方程为:dp[i][v] = max(dp[i-1][v], dp[i-1][v-w[i]]+dp[i])
二、经典例(水)题讲解
提到dp这恐怕是经(超)典(水)的一道题。
洛谷 P1048采药
这种题明显就是背包问题的模板化用,请大家结合上面内容自行理解。
代码如下:
#include<algorithm>
#include <iostream>
#include<cstdio>
using namespace std;
int w[105],val[105];
int dp[105][1005];
int t,m;
int main()
{
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&w[i],&val[i]);
}
for(int i=1;i<=m;i++) //从第一件开始枚举
for (int j=t;j>=0;--j)//背包容量
{
if(j>=w[i])
{
dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);
}
else
{
dp[i][j]=dp[i-1][j];
}
}
printf("%d",dp[m][t]);
return 0;
}
当然,不可能每个题都这么直白,可能稍微有些变形,大家一定要擦亮双眼,找到其中的关键元素:
举个例子 洛谷 P1049 装箱问题(其实也很水)
这个题乍一看,只有大小没有说价值啊?
其实不难发现,我们也可以把它所占的空间视为它的价值。
代码如下:
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
int v,n;
int w[35],val[35];
int dp[35][20000];
int main()
{
scanf("%d%d",&v,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
val[i]=w[i];//核心重点
}
for(int i=1;i<=n;i++)
for (int j=v;j>=0;--j)
{
if(j>=w[i])
{
dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);
}
else
{
dp[i][j]=dp[i-1][j];
}
}
printf("%d",v-dp[n][v]);
return 0;
}