完全背包问题

完全背包问题

标签(空格分隔): 算法知识文档 动态规划


问题描述:

一个旅行者随身携带一个背包,可以放入背包的物品有n种,每种物品的重量和价值分别是 wi,vi w i , v i ,如果背包的最大容量限制是 b b ,每种物品可以放多个,怎样选择放入背包的物品以使得背包的价值最大?不妨设上述wi,vi,b都是整数.

实例:

n=4,b=10 n = 4 , b = 10
v1=1,v2=3,v3=5,v4=9 v 1 = 1 , v 2 = 3 , v 3 = 5 , v 4 = 9
w1=2,w2=3,w3=4,w4=7 w 1 = 2 , w 2 = 3 , w 3 = 4 , w 4 = 7

建模:

解是 <x1,x2,...,xn> < x 1 , x 2 , . . . , x n > <script type="math/tex" id="MathJax-Element-155"> </script>,其中 xi x i 是装入背包的第 i i 种物品个数.

目标函数:
maxi=1nvixi

约束条件:
ni=1xiwib,xiN ∑ i = 1 n x i w i ≤ b , x i ∈ N

线性规划问题:
由线性条件约束的线性函数取最大或者最小的问题.当 xi x i 的取值是正实数.

整数规划问题:
线性规划的变量 xi x i 都是非负整数.在该问题中显然放入的每种物品个数都是整数个.

子问题界定和计算顺序:

子问题界定:
由参数 k k y界定
k k : 考虑对物品1,2,3,...,k的选择,即前 k k 种物品的选择.k[1,n]

y y : 背包总重量不超过y,y[1,b]

原始输入: k=n,y=b. k = n , y = b .

子问题的计算顺序:
k=1,2,3,...,n k = 1 , 2 , 3 , . . . , n
对于给定的 k k , y=1,2,3,...,b

优化函数的递推方程:

Fk(y) F k ( y ) :装前 k k 种物品,总重不超过y,背包达到的最大价值.

递推方程:
Fk(y)=max{Fk1(y),Fk(ywk)+vk} F k ( y ) = max { F k − 1 ( y ) , F k ( y − w k ) + v k }
自然语言描述:
将前 k k 种物品装入背包总重不超过y获得的最大价值等于将前 k1 k − 1 种物品装入背包总重不超过 y y (也就是当前第k种物品不装入背包)获得的最大价值,和将前 k k 种物品装入背包总重量不超过ywk获得的最大价值中的最大价值(也就是装入第 k k 种物品).

Fk(0)=0,k[0,n],F0(y)=0,y[0,b]
自然语言描述:
将前 k k 种物品放入背包总重不超过0获得的最大价值是0.k[0,n]
将前0种物品放入总重不超过 y y 获得的最大价值是0.y[0,b].

F1(y)=yw1,y[1,b],Fk(y)=,y<0 F 1 ( y ) = ⌊ y w 1 ⌋ , y ∈ [ 1 , b ] , F k ( y ) = − ∞ , y < 0
自然语言描述:
将前一个物品放入背包重量不超过 y y ,获得的最大价值是yw1, y y 除以w1向下取整.
y y 小于0的时候,最大效益设置成负无穷.因为递推方程要查找 Fk(ywk) F k ( y − w k ) 的值, ywk y − w k 可能会小于0.

根据投资问题写递推方程:
Fk(y)=min0xkywk{Fk1(ywkxk)+xkvk} F k ( y ) = min 0 ≤ x k ≤ ⌊ y w k ⌋ { F k − 1 ( y − w k x k ) + x k v k }

两个递推方程的比较:
第一种递推方程计算的时间复杂都要小.计算 Fk(y) F k ( y ) 时间复杂度是 O(1) O ( 1 ) ,第二个计算 Fk(y) F k ( y ) 最坏时间复杂度是 O(b) O ( b )

标记函数:
ik(y) i k ( y ) : 装前 k k 种物品,总重不超过y,背包达到最大价值时装入物品的最大标号.

ik(y)={ik1(y),k,Fk1(y)>Fk(ywk)+vk Fk1(y)Fk(ywk)+vk i k ( y ) = { i k − 1 ( y ) , F k − 1 ( y ) > F k ( y − w k ) + v k k ,   F k − 1 ( y ) ≤ F k ( y − w k ) + v k

i1(y)={0,1,y<w1 w1y i 1 ( y ) = { 0 , y < w 1 1 ,   w 1 ≤ y

实例:

输入:
v1=1,v2=3,v3=5,v4=9, v 1 = 1 , v 2 = 3 , v 3 = 5 , v 4 = 9 ,
w1=2,w2=3,w3=4,w4=7 w 1 = 2 , w 2 = 3 , w 3 = 4 , w 4 = 7
b=10 b = 10

计算:
Fk(y): F k ( y ) 的 计 算 表 如 下 :

k/y12345678910
00000000000
10112233445
20133466799
30135568101011
40135569101012

追踪解:

k/y12345678910
00000000000
10111111111
20122222222
30123333333
40123334344

i4(10)=4=>1x4 i 4 ( 10 ) = 4 => 1 ≤ x 4
i4(10w4)=i4(3)=2=>1x2,x4=1,x3=0 i 4 ( 10 − w 4 ) = i 4 ( 3 ) = 2 => 1 ≤ x 2 , x 4 = 1 , x 3 = 0
i2(3w2)=i2(0)=0=>x2=1,x1=0 i 2 ( 3 − w 2 ) = i 2 ( 0 ) = 0 => x 2 = 1 , x 1 = 0

第一个: 装入 0 0
第二个: 装入1
第三个: 装入 0 0
第四个: 装入1

总的重量: 10
总的最大价值: 12

算法时间复杂度: O(nb) O ( n b )

CODE: C O D E :

#include <bits/stdc++.h>

using namespace std;

#define MAX_D 100005 // 最大背包容量
#define MAX_N 105 // 最大物品数目
int w[MAX_N], v[MAX_N], F[MAX_D]; // 节省空间的做法
int n,b; // 物品数目,背包容量

void printAns(){
    for(int i = 0; i <= b; i++){
        printf("%d\t", F[i]);
    }
    printf("\n");
}

void solve(){
    int i,j;
    memset(F, 0, sizeof(int)*(b+1));
    for(i = 1; i <= n; i++){ // 前i个物品
        for(j = w[i]; j <= b; j++){ // 放入背包容量不超过j 
            F[j] = max(F[j], F[j - w[i]]+v[i]);
        }
        printAns();
    }
    printf("%d\n", F[b]);
}

int main(){
    freopen("in.txt", "r", stdin);
    int i;
    while(~scanf("%d%d", &n, &b) && n && b){
        for(i = 1; i <= n; i++) scanf("%d%d", &w[i], &v[i]); // 输入重量和价值
        solve();
    }
    return 0;
}

输入:
4 10
2 1
3 3
4 5
7 9

输出:
0 0 1 1 2 2 3 3 4 4 5
0 0 1 3 3 4 6 6 7 9 9
0 0 1 3 5 5 6 8 10 10 11
0 0 1 3 5 5 6 9 10 10 12
12

实战题目:Investment

Investment
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 11595 Accepted: 4038

Description

John never knew he had a grand-uncle, until he received the notary’s letter. He learned that his late grand-uncle had gathered a lot of money, somewhere in South-America, and that John was the only inheritor.
John did not need that much money for the moment. But he realized that it would be a good idea to store this capital in a safe place, and have it grow until he decided to retire. The bank convinced him that a certain kind of bond was interesting for him.
This kind of bond has a fixed value, and gives a fixed amount of yearly interest, payed to the owner at the end of each year. The bond has no fixed term. Bonds are available in different sizes. The larger ones usually give a better interest. Soon John realized that the optimal set of bonds to buy was not trivial to figure out. Moreover, after a few years his capital would have grown, and the schedule had to be re-evaluated.
Assume the following bonds are available:
Value Annual
interest
4000 400
3000 250

With a capital of e10 000 one could buy two bonds of 4000,givingayearlyinterestof 4 000 , g i v i n g a y e a r l y i n t e r e s t o f 800. Buying two bonds of 3000,andoneof 3 000 , a n d o n e o f 4 000 is a better idea, as it gives a yearly interest of 900.Aftertwoyearsthecapitalhasgrownto 900. A f t e r t w o y e a r s t h e c a p i t a l h a s g r o w n t o 11 800, and it makes sense to sell a 3000oneandbuya 3 000 o n e a n d b u y a 4 000 one, so the annual interest grows to 1050.Thisiswherethisstorygrowsunlikely:thebankdoesnotchargeforbuyingandsellingbonds.Nextyearthetotalsumis 1 050. T h i s i s w h e r e t h i s s t o r y g r o w s u n l i k e l y : t h e b a n k d o e s n o t c h a r g e f o r b u y i n g a n d s e l l i n g b o n d s . N e x t y e a r t h e t o t a l s u m i s 12 850, which allows for three times 4000,givingayearlyinterestof 4 000 , g i v i n g a y e a r l y i n t e r e s t o f 1 200.
Here is your problem: given an amount to begin with, a number of years, and a set of bonds with their values and interests, find out how big the amount may grow in the given period, using the best schedule for buying and selling bonds.

Input

The first line contains a single positive integer N which is the number of test cases. The test cases follow.
The first line of a test case contains two positive integers: the amount to start with (at most 1000000),andthenumberofyearsthecapitalmaygrow(atmost40).Thefollowinglinecontainsasinglenumber:thenumberd(1<=d<=10)ofavailablebonds.Thenextdlineseachcontainthedescriptionofabond.Thedescriptionofabondconsistsoftwopositiveintegers:thevalueofthebond,andtheyearlyinterestforthatbond.Thevalueofabondisalwaysamultipleof 1 000 000 ) , a n d t h e n u m b e r o f y e a r s t h e c a p i t a l m a y g r o w ( a t m o s t 40 ) . T h e f o l l o w i n g l i n e c o n t a i n s a s i n g l e n u m b e r : t h e n u m b e r d ( 1 <= d <= 10 ) o f a v a i l a b l e b o n d s . T h e n e x t d l i n e s e a c h c o n t a i n t h e d e s c r i p t i o n o f a b o n d . T h e d e s c r i p t i o n o f a b o n d c o n s i s t s o f t w o p o s i t i v e i n t e g e r s : t h e v a l u e o f t h e b o n d , a n d t h e y e a r l y i n t e r e s t f o r t h a t b o n d . T h e v a l u e o f a b o n d i s a l w a y s a m u l t i p l e o f 1 000. The interest of a bond is never more than 10% of its value.

Output

For each test case, output – on a separate line – the capital at the end of the period, after an optimal schedule of buying and selling.

Sample Input

1
10000 4
2
4000 400
3000 250

Sample Output

14050

Source

Northwestern Europe 2004

题目分析:

题目大意:
t t 个测试样例,一开始有 m0 的资金,有 d d 种债劵,第 i 债劵的价值是 wi w i ,利息是 vi v i ,求通过买债劵投资 y y 年后得到的最大收益( y 年后有的钱).

样例计算说明:
最开始: 有 m0=10000 m 0 = 10000 元钱,购买一个 w1=4000 w 1 = 4000 两个 w2=3000 w 2 = 3000 的债劵( m m 刚好足够买),此时利息是
r0=v1+2v2=400+2250=900元.

第一年: 有 m1=m0+r0=10900 m 1 = m 0 + r 0 = 10900 元,今年也继续按照上一年的方案购买债劵得到利息: r1=r0=900 r 1 = r 0 = 900

第二年: 有 m2=m1+r1=11800 m 2 = m 1 + r 1 = 11800 元,今年的购买方案是:买两个 w1=4000 w 1 = 4000 , 买一个 w2=3000 w 2 = 3000 , 需要 11000 11000 元,是可以购买的,此时利息 r2=2v1+v2=2400+250=1050 r 2 = 2 v 1 + v 2 = 2 ∗ 400 + 250 = 1050 元.

第三年: 有 m3=m2+r2=12850 m 3 = m 2 + r 2 = 12850 元,今年的购买方案是:买三个 w1=4000 w 1 = 4000 ,需要 12000 12000 元,显然是够的.此时 r3=3400=1200 r 3 = 3 ∗ 400 = 1200 元.

第四年: 有 m4=m3+r3=12850+1200=14050 m 4 = m 3 + r 3 = 12850 + 1200 = 14050 元,就是输出的结果.

样例计算总结:
通过计算样例我们不难发现,每一次得到购买债劵的最优方案使得利息最大就可以得到每一年的最大收益.而得到最优方案的过程就是一个完全背包的模型.

题目难点:
将数据缩小

轻松ACCEPT:

CODE: C O D E :

// http://poj.org/problem?id=2063
#include <cstdio>
#include <cstring>

#define MAX_D 15
#define K1 1000
#define max(a, b) a > b ? a : b;
const int MAX_M = 1e6+5;
int t,y,d,m;
int a,b;
int w[MAX_D],v[MAX_D],F[MAX_M];

void solve(){
    int i,j,_y;
    memset(F, 0, sizeof(F));
    for(_y = 1; _y <= y; _y++){ // 年份
        for(i = 1; i <= d; i++){ // 债劵种类
            for(j = w[i]; j <= a; j++){ // 完全背包
                F[j] = max(F[j], F[ j-w[i] ] + v[i]);
            }
        }
        m += F[a]; // 加上
        a = m/K1; // 缩小数据
    }
    printf("%d\n", m);
}

int main(){
    //freopen("in.txt", "r", stdin);
    int i;
    scanf("%d", &t); // 输入测试样例种数
    while(t--) {
        scanf("%d%d%d", &m, &y, &d); // 输入初始资金,年数,债劵种数
        a = m/K1; // 将数据缩小
        for(i = 1; i <= d; i++)  {
            scanf("%d%d", &w[i], &v[i]);
            w[i] /= K1; // 将数据缩小
        }
        solve();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值