当前位置:   article > 正文

【动态规划】投资问题_投资问题动态规划算法

投资问题动态规划算法

本文利用markdown基于https://blog.csdn.net/qq_41926985/article/details/105627049重写,代码部分为本人编辑

代码要求

应用动态规划方法,求解投资问题,实现下面的例子。

#define MAX_N 4 //最大投资项目数目
#define MAX_M 5 //最大投资钱数(万元)
//f[i][j]的意义:第 i(从 1 开始)个项目投资 j 万元的收益
int f[MAX_N+1][MAX_M+1] = {
    {0,0,0,0,0,0},
    {0,11,12,13,14,15},
    {0,0,5,10,15,20},
    {0,2,10,30,32,40},
    {0,20,21,22,23,24}
}; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

投资问题

什么是投资问题

m m m元钱, n n n项投资, f i ( x ) f_i(x) fi(x):将x元投入第i个项目的效益。求使得的总效益最大的投资方案。

举个例子:现在有两个项目x是钱数(单位:元),

f i ( x ) f_i(x) fi(x):将x元钱投资到第i个项目产生的效益

注意:使用的是总共的x钱数投资两个项目,而不是分别投资。

alt

将0元投资这两个项目,则最大收益就是0

将1元投资这两个项目,不难看出

f 1 ( 1 ) + f 2 ( 0 ) = 11 f_1(1)+f_2(0)=11 f1(1)+f2(0)=11

,是最大收益

将2元投资这两个项目,不难看出

f 1 ( 2 ) + f 2 ( 0 ) = 12 f_1(2)+f_2(0)=12 f1(2)+f2(0)=12

,是最大收益

将3元投资这两个项目,

m a x ( f 1 ( 0 ) + f 2 ( 3 ) , f 1 ( 1 ) + f 2 ( 2 ) , f 1 ( 2 ) + f 2 ( 1 ) , f 1 ( 3 ) + f 2 ( 0 ) ) = f 1 ( 1 ) + f 2 ( 2 ) = 16 max(f_1(0)+f_2(3),f_1(1)+f_2(2),f_1(2)+f_2(1),f_1(3)+f_2(0)) =f_1(1)+f_2(2)=16 max(f1(0)+f2(3)f1(1)+f2(2)f1(2)+f2(1)f1(3)+f2(0))=f1(1)+f2(2)=16

,是最大收益

同样的用4元或者5元投资这两个项目,所带来的最大收益分别是21和26

我们接下来对问题进行建模:

目标函数:利用所分配的投资产生最大效益

约束条件:是在总资金的条件下进行投资

alt

建模

问题的解是向量 < x 1 , x 2 , ⋅ ⋅ ⋅ , x n > <x_1,x_2,···,x_n> <x1,x2,⋅⋅⋅,xn>

x i x_i xi是投给项目的钱数, i = 1 , 2 , ⋅ ⋅ ⋅ , n . i=1,2,···,n. i=1,2,⋅⋅⋅,n.

目标函数 m a x { f 1 ( x 1 ) + f 2 ( x 2 ) + ⋅ ⋅ ⋅ + f n ( x n ) } max \{f_1(x_1)+f_2(x_2)+···+f_n(x_n)\} max{f1(x1)+f2(x2)+⋅⋅⋅+fn(xn)}

约束条件: x 1 + x 2 + ⋅ ⋅ ⋅ + x n = m , x i ∈ N x_1+x_2+···+x_n=m,x_i\in N x1+x2+⋅⋅⋅+xn=m,xiN

下面我们来考虑用动态规划算法来解投资问题

动态规划算法来解投资问题

子问题的界定:由参数 k k k x x x界定

k k k:考虑对每个项目 1 , 2 , … , k 1,2,…,k 12k的投资

x x x:投资总钱数不超过 x x x

接下来我们再看一下递推方程

F k ( x ) : x F_k(x):x Fk(x):x元钱投给前k个项目的最大效益

我们可以这么想,假如我们要求 F k ( x ) F_k(x) Fk(x),即就是求x元钱投给前 k k k个项目的最大效益。那不妨求 p p p元钱 ( p ≤ x ) (p\leq x) (px)投给前k-1个项目的最大效益 F k − 1 ( p ) F_{k-1}(p) Fk1(p),进而确定 F k ( x ) F_k(x) Fk(x)

我们进而可以列出递推方程:

alt

我们看到啊, F k ( x ) F_k(x) Fk(x)的求解,就是去求用 x − x k x-x_k xxk分配前 k − 1 k-1 k1个项目所产生的最大效益。然而这个最大效益是在备忘录存着来。没错备忘录的作用就是存储最大的效益。
F 1 ( x ) F_1(x) F1(x):就是在投资表中的用x钱投资第一个项目的收益

接下来我们看个例子:
这是一个投资——效益表

ALT

我们要先明确最小子问题是什么,然后才能从这个最小子问题开始算起;然后考虑计算顺序,保证后面的值在前面已经计算好。

这里我们看到第一个项目的最大收益就是投资对应的收益,即
F 1 ( 0 ) = 0 , F 1 ( 1 ) = 11 , F 1 ( 2 ) = 12 , F 1 ( 3 ) = 13 , F 1 ( 4 ) = 14 , F 1 ( 5 ) = 15 。 (1) F_1(0)=0,F_1(1)=11,F_1(2)=12,F_1(3)=13,F_1(4)=14,F_1(5)=15。\tag{1} F1(0)=0F1(1)=11F1(2)=12F1(3)=13F1(4)=14F1(5)=15(1)

我们能看到啊,前1个项目可以定为最小子问题,它的初值可以通过查表得到,不用计算。而后面的项目随着项目的增多,子问题的复杂性就会增强。因此我们根据项目序列的递增关系来计算,从而保证后面的值在前面已经计算好了。

我们通过上面的递推方程可以得知,用 x − x k x-x_k xxk分配前 k − 1 k-1 k1个项目所产生的最大效益越大以及x元钱投给前k个项目的最大效益越大,从而使得原问题的解达到最大。进而满足依赖关系。而对于 x k x_k xk为何值,这个是需要通过计算获取最优解来得到。

好,我们来继续看前两个项目的最大效益:
我们先看 x = 0 x=0 x=0,则最大效益是0

再来看 x k = 1 x_k=1 xk=1 F 2 ( 1 ) = m a x { f 2 ( 1 ) + F 1 ( 1 − 1 ) , f 2 ( 0 ) + F 1 ( 1 − 0 ) } = 11 F_2(1)=max\{f_2(1)+F_1(1-1),f_2(0)+F_1(1-0)\}=11 F2(1)=max{f2(1)+F1(11)f2(0)+F1(10)}=11
再来看 x k = 2 x_k=2 xk=2 F 2 ( 2 ) = m a x { f 2 ( 2 ) + F 1 ( 2 − 2 ) , f 2 ( 1 ) + F 1 ( 2 − 1 ) , f 2 ( 0 ) + F 1 ( 2 − 0 ) } = 12 F_2(2)=max\{f_2(2)+F_1(2-2),f_2(1)+F_1(2-1),f_2(0)+F_1(2-0)\}=12 F2(2)=max{f2(2)+F1(22)f2(1)+F1(21)f2(0)+F1(20)}=12
再来看 x k = 3 x_k=3 xk=3 F 2 ( 3 ) = m a x { f 2 ( 3 ) + F 1 ( 3 − 3 ) , f 2 ( 2 ) + F 1 ( 3 − 2 ) , f 2 ( 1 ) + F 1 ( 3 − 1 ) , f 2 ( 0 ) + F 1 ( 3 − 0 ) } = 16 F_2(3)=max\{f2(3)+F_1(3-3),f_2(2)+F_1(3-2),f_2(1)+F_1(3-1),f_2(0)+F_1(3-0)\}=16 F2(3)=max{f2(3)+F1(33)f2(2)+F1(32)f2(1)+F1(31)f2(0)+F1(30)}=16
同样的 F 2 ( 4 ) = 21 , F 2 ( 5 ) = 26 F_2(4)=21,F_2(5)=26 F2(4)=21F2(5)=26

当然,这里得到的 F 2 ( 1 ) , F 2 ( 2 ) , F 2 ( 3 ) , F 2 ( 4 ) , F 2 ( 5 ) F_2(1),F_2(2),F_2(3),F_2(4),F_2(5) F2(1)F2(2)F2(3)F2(4)F2(5)要记录到备忘录里面。

那么如何去记录解?
我们用 s s s数组来记录解。我们去记录在得到最大效益的时候,最后一个项目给了多少钱。
就如同上面的例子,在前两个项目的最大收益中。
x i ( x ) : x_i(x): xi(x):分配x元钱给前i个项目,在最大收益时,第i个项目得到了多少钱
x 2 ( 1 ) : x_2(1): x2(1):看到啊, F 2 ( 1 ) = f 2 ( 0 ) + F 1 ( 1 − 0 ) = 11 F_2(1)=f_2(0)+F_1(1-0)=11 F2(1)=f2(0)+F1(10)=11。此时,第2个项目得到了0元钱
x 2 ( 2 ) : f 2 ( 0 ) + F 1 ( 2 − 0 ) = 12 x_2(2):f_2(0)+F_1(2-0)=12 x2(2):f2(0)+F1(20)=12。此时,第2个项目得到了0元钱
x 2 ( 3 ) : f 2 ( 2 ) + F 1 ( 3 − 2 ) = 16 x_2(3):f_2(2)+F_1(3-2)=16 x2(3):f2(2)+F1(32)=16。此时,第2个项目得到了2元钱
同样的,我们也能得到 x 2 ( 4 ) = 3 , x 2 ( 5 ) = 4 x_2(4)=3,x_2(5)=4 x2(4)=3x2(5)=4

ok,下面介绍一下如何追踪解
上面的投资问题的结果如图所示:

alt

我们细想,原问题是用5元钱分配所有项目(这里就是4个项目),所得到的最大收益
这个最大收益是不是就是 F 4 ( 5 ) F_4(5) F4(5)
( F 4 ( 5 ) F_4(5) F4(5):用5元钱分配前4个项目得到的最大收益)
,那这个值就可以去衡量原问题的解。因此我们追踪解也要从 x 4 ( 5 ) x_4(5) x4(5)开始,自底向上追踪。

先看到x4(5)=1,说明达到最大收益的时候分配给最后一个项目,即第4个项目是1元钱。
那么第3个项目呢?
第3个项目就是 x 3 ( x ) x_3(x) x3(x),这个x就是5-1=4,就是用总共的5元钱-分配给第4个项目的钱数。
x 3 ( 5 − 1 ) = 3 x_3(5-1)=3 x3(51)=3。因此在得到最大收益时,分配给第3个项目3元钱。
同理 x 2 ( 4 − 3 ) = 0 , x 1 ( 1 − 0 ) = 1 x_2(4-3)=0,x_1(1-0)=1 x2(43)=0x1(10)=1
也许你会问为什么要这么解?
我们看那个递推方程,我们既然知道 F 4 ( x 5 ) = f 4 ( x 4 ) + F 3 ( x − x 4 ) F_4(x_5)=f_4(x_4)+F_3(x-x_4) F4(x5)=f4(x4)+F3(xx4)。然而我们知道了在最大收益时,分配给第4个项目1元钱,这个可以通过代码可以实现。

F 3 ( 4 ) F_3(4) F3(4),这个通过查表即可得到41,此时分配给它的钱就是3。同样的也可以逆推出 F 2 F_2 F2 F 1 F_1 F1中的 x 2 和 x 1 x_2和x_1 x2x1。就是通过前k-1个项目的最大收益+用剩下钱分配给第k个项目的收益。然而前 k − 1 k-1 k1个项目的最大收益是保存在了我们的备忘录中,所以这个值不仅可以查到,而且它只计算了一次。没有重复计算。使得这个唯一确定的值+ f 4 ( x 4 ) f_4(x_4) f4(x4)值就是最大收益。

因此我们抛去 f 4 ( x 4 ) f_4(x_4) f4(x4)的值,也就是前k个项目的最大收益 F 3 ( x − x 4 ) F_3(x-x_4) F3(xx4)

因此我们可以通过查表得到 x 3 ( x − x 4 ) x_3(x-x_4) x3(xx4)。故这样计算是合理的

#include<bits/stdc++.h>
using namespace std;
#define MAX_N 4 //最大投资项目数目
#define MAX_M 5 //最大投资钱数(万元)
//f[i][j]的意义:第 i(从 1 开始)个项目投资 j 万元的收益
int f[MAX_N+1][MAX_M+1] = {
    {0,0,0,0,0,0},
    {0,11,12,13,14,15},
    {0,0,5,10,15,20},
    {0,2,10,30,32,40},
    {0,20,21,22,23,24}
}; 

void printNum(int num[MAX_N+1][MAX_M+1]){
    for(int i=0;i<=MAX_M;i++)
    {
        for(int j=0;j<=MAX_N;j++)
        {
            cout << num[j][i] << " ";
        }
        cout << endl;
    }
    return;
}

int main()
{
    int s[MAX_N+1][MAX_M+1] = {0};//s数组用于记录最后一个项目分配的钱
    int F[MAX_N+1][MAX_M+1] = {0};
    for(int i=0;i<=MAX_M;i++){
        F[1][i] = f[1][i];
        s[1][i] = i;
    }

    
    for(int i=2;i<=MAX_N;i++)
    {
        for(int j=0;j<=MAX_M;j++)
        {
            int max = F[i-1][j];
            int cnt = 0;
            for(int k=0;k<=j;k++)
            {
                int num = f[i][k]+F[i-1][j-k];
                if(num>=max)
                {
                    max = num;
                    cnt = k;
                }
            }
            F[i][j] = max;
            s[i][j] = cnt;
        }
    }
    

    printNum(f);
    cout << endl;
    printNum(s);
    cout << endl;
    printNum(F);
    cout << endl;

    int res = s[MAX_N][MAX_M];
    int pro = MAX_N;
    int m = MAX_M;

    while(pro>0)
    {
        cout << "第" << pro << "个项目的钱数为:" << res << endl;;
        pro-=1;
        m-=res;
        res = s[pro][m];
    }
    return 0;
}
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/707686
推荐阅读
相关标签
  

闽ICP备14008679号