问题: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();
}
}
结果截图: