投资问题-动态规划

问题:m元钱,投资n个项目,效益函数fi(x),表示i个项目投资x元的收益,求如何分配每个项目的钱数使得总效益最大?

由交叠的子问题构成且满足最优解法,所以选择动态规划进行求解。

建模:
   输入:
     m,n,fi(x)
      i = 1,2,3,4…n(表第i个项目)
      x = 1,2,3,4…m(表投资x元)
   输出:
      x1,x2,x3…xi(表第i个项目所投资的钱)

有上述条件可得

  约束条件:x1+x2+x3+…+xi <= m
  目标函数:max{f1(x1)+f2(x2)+f3(x3)+…+fi(xi)}
  总结为表示在满足约束条件的情况下获得的最大目标函数

最优子结构性质:
  该投资问题具有最优子结构性质.
  设Vi(x)为分配前i个项目,资金为x的最大利润.

当i=1时,V1(x)=f1(x)

当1<i<=n时, 若分配给第i个项目的钱为y时,则剩下的钱为x-y,给前i-1个项目,因为具有最优子结构,所以前i-1个项目的最大利润就为Vi-1(x-y).

所以Vi(x)的最大利润为 Vi-1(x-y)+fi(y)

递归关系:
    设dp[i][j]表示投资前i(0<=i<=n)个项目,使用资金j(0<=j<=m)元的总利润,我们先从第一个项目考虑,然后考虑前两个项目,然后前三个项目,到第k个项目时,为k分配w元钱,m-w元钱的最大效益为dp[i-1][m-w],这样我们可以得到递推方程:

       dp[i][j]=max{dp[i-1][j-xi]+fi(xi)}(0<=i<=n)

伪代码:

For(int i=1; i<=n; i++//前i个项目
	For(int j=1; j<=m;j++{//总投入j元
				dp[i][j] = 0;
		 For(int c=0;c<=j;c++{//投入项目i为c元
				if(dp[i-1][j-c]+fi(c)>dp[i][j]){
						dp[i][j] = dp[i-1][j-c]+fi(c)
						}
			}
	}

最终实现代码:

public class InvestmentProblem {    //投资问题
    public static void main(String[] args) {
        int[][] profit = {
                {0,0,0,0,0,0},  //不投项目所获得利益
                {0,11,12,13,14,15},   //第一个项目投入0,1,2,3,4,5万元,搜获得的收益
                {0,3,4,6,7,8},    //第二个项目投入0,1,2,3,4,5万元,搜获得的收益
                {0,2,5,8,12,23},  //第三个项目投入0,1,2,3,4,5万元,搜获得的收益
                {0,4,6,9,12,15}   //第四个项目投入0,1,2,3,4,5万元,搜获得的收益
        };
        int[][] dp = new int[profit.length][profit[0].length];     //dp[i][j],表示在前i个项目,在j元内的最大利润,0<=j<=m
        int[][] path = new int[profit.length][profit[0].length];
        for (int j = 0; j < dp[0].length; j++) {//初始化dp[0][j] = 0,表示不投项目所获得利益,0<j<=m
            dp[0][j] = 0;
            path[0][j] = 0;
        }
        for (int i = 0; i < dp.length; i++) {//初始化dp[i][0] = 0,表示在前i个项目,再投资0元所获得的利润,0<=i<=n
            dp[i][0] = 0;
            path[i][0] = 0;
        }
        for(int i = 1; i < profit.length; i++){ //前i个项目
            for (int j = 1; j < profit[0].length; j++) { //总投入j元
                dp[i][j] = 0;   //初始化dp[i][j] = 0
                path[i][j] = 0;
                for (int k = 0; k <= j; k++) { //投入项目i为k元
                    if (dp[i-1][j-k]+profit[i][k] > dp[i][j]){  //取最大的利润存入dp[i][j]
                        dp[i][j] = dp[i-1][j-k]+profit[i][k];
                        path[i][j] = k;
                    }
                }
            }
        }
        for (int i = 1; i < dp.length; i++) {   //打印dp
            for (int j = 1; j < dp[0].length; j++) {
                System.out.print(dp[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println("最大利润为"+dp[dp.length-1][dp[0].length-1]+"万元");   //输出最大利润
        
        traceback(profit.length-1,profit[0].length-1,path);
    }

    public static void traceback(int pNum, int mAmount, int[][] t){ //递归打印

        if(pNum == 0 || mAmount == 0) return;

        traceback(pNum -1, mAmount - t[pNum][mAmount],t);

        System.out.print("项目" + pNum + "投资: " + t[pNum][mAmount]);;
        System.out.println();
    }
}

结果截图:
在这里插入图片描述

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值