动态龟划规划之背包问题
01背包
01背包的特点:
物品只有一件,可以选择放或者不放。
问题:
有N件物品和一个容量为V的背包。放入第
i
i
i件物品耗费的费用是
C
i
C_i
Ci,得到的价值是
W
i
W_i
Wi。求解将哪些物品装入背包可以使物品的价值总和最大。
思路:
把整个问题,拆分成几个子问题。则面对第
i
i
i个物品时,只需要考虑放或者不放。
所以不妨定义一个状态:即
F
[
i
,
v
]
F[i,v]
F[i,v]表示前
i
i
i件物品放入容量为
v
v
v的背包获得的最大价值。
所以可以得到状态方程:
F
[
i
,
v
]
=
m
a
x
{
F
[
i
−
1
,
v
]
,
F
[
i
−
1
,
v
−
C
i
]
+
W
i
}
F[i,v]=max\{F[i-1,v],F[i-1,v-C_i]+W_i\}
F[i,v]=max{F[i−1,v],F[i−1,v−Ci]+Wi}
下面解释这个状态方程。
初始状态如下图(不要问为什么字写这么丑):
第一行全部初始化为0,表示将前0件物品装入背包(即没有装入物品)时的最大价值为0。
面对第
i
i
i个物品,只需要考虑装或不装两种情况。
若不装,则:
F
[
i
,
v
]
=
F
[
i
−
1
,
v
]
F[i,v]=F[i-1,v]
F[i,v]=F[i−1,v]
若装,则:
F
[
i
,
v
]
=
F
[
i
−
1
,
v
−
C
i
]
+
W
i
F[i,v]=F[i-1,v-C_i]+W_i
F[i,v]=F[i−1,v−Ci]+Wi
前提是背包装的下第
i
i
i个物品,即
v
≥
C
i
v≥C_i
v≥Ci
所以只需要考虑两个中的最大值即可。
其实这个思路有点像之前学过的贪心算法
但是不同的是:
贪心保证求到的是 局部最优解
但是背包问题中,当前状态下的局部最优解组成的解并不一定是全局最优解,当前的局部最优解受之前的选择的影响
所以,这个过程其实有点像之前学过的记忆化搜索,即保存每一步的状态,之后用到这一步时只需要进行读取操作,不需要重复计算。(有点像求最短路)
多说无益,上一道题。
HDU2602
经典的01背包问题,套板子可以直接过。
题解代码:
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
int main()
{
int DP[1000][1000];
int Value[1000],Cost[1000];
int N,A,B;//A是骨头数,B是背包容量
cin>>N;
while(N)
{
memset(DP,0,sizeof(DP));
cin>>A>>B;
for(int i=1; i<=A; i++)
{
cin>>Value[i];
}
for(int i=1; i<=A; i++)
{
cin>>Cost[i];
}
for(int i=1; i<=A; i++)
{
for(int j=0; j<=B; j++)
{
if(j>=Cost[i])
DP[i][j]=max(DP[i-1][j],DP[i-1][j-Cost[i]]+Value[i]);
else
DP[i][j]=DP[i-1][j];
}
}
cout<<DP[A][B]<<endl; 但是这个地方我不明白为什么是
N--; 输出DP[A][B];
} 我总认为背包不一定会恰好装满。
return 0;
}
完全背包
完全背包的特点:物品有无穷件,要考虑的不是第
i
i
i件物品拿不拿而是拿几件
题目:有N种物品和一个容量为V的背包。每种物品有无限件,放入第
i
i
i种物品耗费的费用是
C
i
C_i
Ci,得到的价值是
W
i
W_i
Wi。求解将哪些物品装入背包可以使物品的价值总和最大。
与01背包不同的是,每种物品可以装的个数是
0~
V
/
C
i
(
向
下
取
整
)
V/C_i(向下取整)
V/Ci(向下取整)
所以类似的,可以得到状态方程:
F
[
i
,
v
]
=
m
a
x
{
F
[
i
−
1
,
v
−
k
C
i
]
+
k
W
i
}
∣
(
0
≤
k
C
i
≤
v
)
F[i,v]=max\{F[i-1,v-kC_i]+kW_i\}|(0≤kC_i≤v)
F[i,v]=max{F[i−1,v−kCi]+kWi}∣(0≤kCi≤v)
也就转化成了01背包
不说废话,上题:
HDU2159
题意:
耐久度为背包体积,要杀的怪为物品种类,得到的经验为最后的总价值,杀的小怪数为拿的物品的总和的上限。
最后只需要从小到大遍历,找到第一个得到的经验大于所需经验的格子输出即可。
题解: