首页 > 代码库 > 动态规划:背包问题

动态规划:背包问题

例题:(from: http://www.wikioi.com/problem/1014/)

有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入描述
一个整数v,表示箱子容量
一个整数n,表示有n个物品
接下来n个整数,分别表示这n 个物品的各自体积
输出描述
一个整数,表示箱子剩余空间。
样例输入
24  6
8 3 12 7 9 7
样例输出
0
 1 #include <iostream>
 2 using namespace std;
 3 int maxUsed[200001];
 4 
 5 int main(){
 6     int V, N;
 7     cin >> V >> N;
 8     int v;
 9     while(N--){
10         cin >> v;
11         for(int curV = V; curV >= v; --curV){
12             maxUsed[curV] = max(maxUsed[curV], maxUsed[curV-v] + v);
13         }
14     }
15     cout << V - maxUsed[V] << endl;
16     retrn 0;
17 }
Code

解析:动态方程:A(v) = max{A(v-vi)+vi,A(v)} ,(v ≥ vi) 。 意义:放入 i,若有更优解,则更新。 【 A(v)表示总体积为v时的最大放入体积 】

如图:

 引申:

m(i,v)表示当前第 i 个物体,最大体积为 v 时的最大重量。

解析如图:  

 追踪路径过程:

?
1
2
3
4
5
6
7
8
9
void TracePath(int V, int N, bool *path){
    for(int i = N; i > 1; --i){
        if(maxW[i][V] == maxW[i-1][V]) path[i] = false;
        else {path[i] = true; V -= vv[i];} //vv[i]为 i 的体积
    }
    path[1] = (maxW[0][V]) ? false : true;
    for(int i = 1; i <= N; ++i) cout << path[i] << " ";
    cout << endl;
}

 

另一例题:( from :http://www.wikioi.com/problem/1068/ )

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一 的起点,第N-1格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。0 1 2 3 4 5 ……N-1。 
乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型 的卡片,见样例),每种类型的卡片上分别标有1、23、4四个数字之一,表示使用这种卡 片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择 一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。 游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到 该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的 分数总和。 很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡 片使用顺序使得最终游戏得分最多。 现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到 多少分吗?
输入描述
输入的每行中两个数之间用一个空格隔开。 
第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。 
第2行N个非负整数,a1a2……aN,其中ai表示棋盘第i个格子上的分数。 
第3行M个整数,b1b2……bM,表示M张爬行卡片上的数字。 输入数据保证到达终点时刚好用光M张爬行卡片,即N - 1=∑(1->M) bi.
输出描述
输出一行一个整数
样例输入
13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1
样例输出
455
数据范围
对于30%的数据有1 ≤ N≤ 301 ≤M≤ 12。
对于50%的数据有1 ≤ N≤ 1201 ≤M≤ 50,且4 种爬行卡片,每种卡片的张数不会超过20。
对于100%的数据有1 ≤ N≤ 3501 ≤M≤ 120,且4 种爬行卡片,每种卡片的张数不会超过40;0 ≤ ai ≤ 1001 ≤ i ≤ N;1 ≤ bi ≤ 4,1 ≤ i ≤M。

code:

+ View Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <algorithm>
using namespace std;
int sum[41][41][41][41]; // number of cards if no more than 40
int total[4]; // amount of card in different type
int number[350]; // score in grids
 
inline int position(int a, int b, int c, int d)
{
    return (a + b*2 + c*3 + d*4);
}
 
/* sum(a, b, c, d) = max{sum(a, b, c, d), sum(a-1, b, c, d) + number[pos + 1], sum(a, b-1, c, d) + number[pos + 2],  */
/* sum(a, b, c-1, d) + number[pos + 3], sum(a, b, c, d-4) + number[pos + 4]}:意义,用去卡片a, b, c, d张时的最大和。  */
 
int main(){
    int N, M;
    cin >> N >> M;
    for(int i = 0; i < N; ++i) cin >> number[i];
    int cardNumber;
    for(int i = 0; i < M; ++i) { cin >> cardNumber; total[cardNumber-1]++; }
    sum[0][0][0][0] = number[0];
    for(int a = 0; a <= total[0]; ++a){
        for(int b = 0; b <= total[1]; ++b){
            for(int c = 0; c <= total[2]; ++c){
                for(int d = 0; d <= total[3]; ++d){
                    if(a > 0) sum[a][b][c][d] = max(sum[a][b][c][d], sum[a-1][b][c][d] + number[position(a-1, b, c, d)+1]);
                    if(b > 0) sum[a][b][c][d] = max(sum[a][b][c][d], sum[a][b-1][c][d] + number[position(a, b-1, c, d) + 2]);
                    if(c > 0) sum[a][b][c][d] = max(sum[a][b][c][d], sum[a][b][c-1][d] + number[position(a, b, c-1, d) + 3]);
                    if(d > 0) sum[a][b][c][d] = max(sum[a][b][c][d], sum[a][b][c][d-1] + number[position(a, b, c, d-1) + 4]);
                }
            }
        }
    }
    cout << sum[total[0]][total[1]][total[2]][total[3]] << endl;
    return 0;
}