【动态规划】投资问题

news2024/12/26 23:45:41

本文利用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}
}; 

投资问题

什么是投资问题

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1643534.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文献阅读:SPACEL:基于深度学习的空间转录组结构表征

文献介绍 「文献题目」 SPACEL: deep learning-based characterization of spatial transcriptome architectures 「研究团队」 瞿昆&#xff08;中国科学技术大学&#xff09; 「发表时间」 2023-11-22 「发表期刊」 Nature Communications 「影响因子」 16.6 「DOI」 10.…

ICode国际青少年编程竞赛- Python-1级训练场-基础训练2

ICode国际青少年编程竞赛- Python-1级训练场-基础训练2 1、 a 4 # 变量a存储的数字是4 Dev.step(a) # 因为变量a的值是4&#xff0c;所以Dev.step(a)就相当于Dev.step(4)2、 a 1 # 变量a的值为1 for i in range(4):Dev.step(a)Dev.turnLeft()a a 1 # 变量a的值变为…

[C++初阶]string类

1. 为什么要学习string类 1.1 C语言中的字符串 C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c; 但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP(面向对象)的思想&…

JAVA中的lambda表达式(无废话)

Lambda表达式是Java SE 8中一个重要的新特性。 它是一种语法形式&#xff0c;可以代码书写更加精炼。 用人话说就是把原来的代码变得很短。 这部分的内容是非常简单的。 一、函数式接口 想要理解lambda表达式&#xff0c;首先要了解函数式接口。 关于接口的知识请查阅&am…

浏览器中不能使用ES6的扩展语法...报错

浏览器大多数已经支持ES6&#xff08;ECMAScript 2015&#xff09;的扩展语法&#xff08;...&#xff09;&#xff0c;包括Chrome、Firefox、Safari和Edge等。然而&#xff0c;如果你在某些浏览器中遇到无法使用扩展语法的问题&#xff0c;可能是由以下原因导致的&#xff1a;…

ngrinder项目-本地调试遇到的坑

前提-maven mirrors配置 <mirrors><!--阿里公有仓库--><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</ur…

每周打靶VulnHub靶机-DOUBLETROUBLE_ 1

doubletrouble: 1靶机传送门 get flags 靶机名为 双重麻烦&#xff0c;可能会繁琐一点 1.信息搜集 使用nmap进行域内存活主机扫描继续扫描其开放端口开放了22(ssh)、80(http)端口使用浏览器访问其80端口是一个登录页面&#xff0c;继续扫描其 敏感目录dirsearch -u [http://19…

通过helm在k8s上安装minio

1 helm安装minio 1.1 下载minio 添加仓库 helm repo add bitnami https://charts.bitnami.com/bitnami 将minio拉取下来 helm pull bitnami/minio --version 版本号 解压到本地开始编辑配置文件 tar -zxf minio-xxx.tgz [rootk8s-master01 minio]# vi values.yaml 1.2…

拼多多多多搜索推广技巧

拼多多多多搜索推广技巧主要包括以下几个方面&#xff1a; 拼多多推广可以使用3an推客。3an推客&#xff08;CPS模式&#xff09;给商家提供的营销工具&#xff0c;由商家自主设置佣金比例&#xff0c;激励推广者去帮助商家推广商品链接&#xff0c;按最终有效交易金额支付佣金…

Docker新建容器 修改运行容器端口

目录 一、修改容器的映射端口 二、解决方案 三、方案 一、修改容器的映射端口 项目需求修改容器的映射端口 二、解决方案 停止需要修改的容器 修改hostconfig.json文件 重启docker 服务 启动修改容器 三、方案 目前正在运行的容器 宿主机的3000 端口 映射 容器…

重要!!!方法的进阶使用------回调函数

参考资料&#xff1a; 参考视频 下面所有举的例子都在参考demo中 概述&#xff1a; 回调函数很简单&#xff0c;就是对普通方法参数的类型的拓展&#xff0c;其实是对普通方法的深层应用&#xff1b;回调函数其实就是将含有执行方法类的实例&#xff0c;以参数的形式传入到方…

集成学习算法:AdaBoost详解以及代码实现

本文尽量从一个机器学习小白或是只对机器学习算法有一个大体浅显的视角入手&#xff0c;尽量通俗易懂的介绍清楚AdaBoost算法&#xff01; 一、AdaBoost简介 AdaBoost&#xff0c;是英文"Adaptive Boosting"&#xff08;自适应增强&#xff09;的缩写&#xff0c;由…

【Linux】进程间通信 - 管道

文章目录 1. 进程间通信介绍1.1 进程间通信目的1.2 进程间通信发展1.3 进程间通信分类 2. 管道2.1 什么是管道2.2 匿名管道2.3 用 fork 来共享管道原理2.4 站在文件描述符角度 - 深入理解管道2.5 站在内核角度 - 管道本质2.6 管道读写规则2.7 管道特点 3. 命名管道3.1 匿名管道…

运行时数据区-基础

运行时数据区-基础 为什么学习运行时数据区Java内存区域&#xff08;运行时数据区域&#xff09;和内存模型&#xff08;JMM&#xff09; 区别组成部分&#xff08;jdk1.7 / jdk1.8&#xff09;从线程隔离性分类与类加载的关系每个区域的功能参考文章 为什么学习运行时数据区 …

【云原生】Docker 的网络通信

Docker 的网络通信 1.Docker 容器网络通信的基本原理1.1 查看 Docker 容器网络1.2 宿主机与 Docker 容器建立网络通信的过程 2.使用命令查看 Docker 的网络配置信息3.Docker 的 4 种网络通信模式3.1 bridge 模式3.2 host 模式3.3 container 模式3.4 none 模式 4.容器间的通信4.…

【翻译】REST API

自动伸缩 API 创建或更新自动伸缩策略 API 此特性设计用于 Elasticsearch Service、Elastic Cloud Enterprise 和 Kubernetes 上的 Elastic Cloud 的间接使用。不支持直接用户使用。 创建或更新一个自动伸缩策略。 请求 PUT /_autoscaling/policy/<name> {"rol…

c语言:打印任意行数的菱形

例如&#xff1a;以下图片形式 #include <stdio.h> int main() {int line 0;scanf_s("%d", &line);int i 0;//打印上半部分for (i 0; i < line; i){//打印空格数int j 0;for (j 0; j < line - 1 - i; j){printf(" ");}//打印*数量for…

vue3(实现上下无限来往滚动)

一、问题描述 一般在大屏项目中&#xff0c;很常见的效果&#xff0c;就是容器中的内容缓慢地向下移动&#xff0c;直到底部停止&#xff0c;然后快速滚动回顶部&#xff0c;然后接着缓慢滚动到底部。并且在特定的情况下&#xff0c;还需要进行一些小交互&#xff0c;那就还得让…

【Linux 进程】 自定义shell

目录 关于shell 1.打印提示符&&获取用户命令字符​编辑 2.分割字符串 3.检查是否为内建命令 cd命令 export命令 echo命令 1.输出最后一个执行的命令的状态退出码&#xff08;返回码&#xff09; 2.输出指定环境变量 4.执行外部命令 关于shell Shell 是计算机操…

【精】hadoop、HIVE大数据从0到1部署及应用实战

目录 基本概念 Hadoop生态 HIVE hdfs(hadoop成员) yarn(hadoop成员) MapReduce(hadoop成员) spark flink storm HBase kafka ES 实战 安装并配置hadoop 环境准备 准备虚拟机 安装ssh并设置免密登录 安装jdk 安装、配置并启动hadoop 添加hadoop环境变量&…