华为机试练习_16:购物单(有依赖的背包问题)

1、题目描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、思路分析

1、本题属于依赖背包问题:
在这里插入图片描述

有四种情况:
只取主件:f[i-1,j-a[i,0]]+a[i,0]*b[i,0]
取主件+附件1:f[i-1,j-a[i,0]-a[i,1]]+a[i,0]*b[i,0]+a[i,1]*b[i,1]
取主件+附件2:f[i-1,j-a[i,0]-a[i,2]]+a[i,0]*b[i,0]+a[i,2]*b[i,2]
既主件+附件1+附件2:f[i-1,j-a[i,0]-a[i,1]-a[i,2]]+a[i,0]*b[i,0]+a[i,1]*b[i,1]+a[i,2]*b[i,2]

含义:
     f[i,j]表示用j元钱,买第i类物品,所得的最大价值
     a[i,0]表示第i类物品主件的价格
     a[i,1]表示第i类物品第1个附件的价格
     a[i,2]表示第i类物品第2个附件的价格
     b[i,0],b[i,1],b[i,2]分别表示主件、第1个附件和第2个附件的重要度。
需要在满足金额money内,买到最大价值的物品。价值=价格*重要度

2、使用二维数组加状态方程:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            //购买物品的总钱数
            int N = scanner.nextInt();

            //希望购买的物品的数量
            int M = scanner.nextInt();

            //二维数组,第i行表示第i个物品的价值,列:0表示主键,1表示附件1,2表示附件2
            int[][] V = new int[M + 1][3];

            //二维数组,第i行表示第i个物品的重要程度,列:0表示主键,1表示附件1,2表示附件2
            int[][] P = new int[M + 1][3];

            for (int i = 1; i <= M; i++) {
                int v = scanner.nextInt();
                int p = scanner.nextInt();
                int q = scanner.nextInt();


                if (q == 0) {   //q=0代表是主件
                    V[i][0] = v;
                    P[i][0] = p;
                } else {      //q>0代表是附件,q代表附件所属的主件编号
                    if (V[q][1] == 0) {
                        V[q][1] = v;
                        P[q][1] = p;
                    } else {
                        V[q][2] = v;
                        P[q][2] = p;
                    }
                }
            }

            //下面正式开始动态规划算法
            int[][] dp = new int[M + 1][N + 1];
            /**
             * 1、钱够用的时候,买不买主键
             * 2、钱够用的时候,买不买主键、附件1
             * 3、钱够用的时候,买不买主键、附件2
             * 4、钱够用的时候,买不买主键、附件1、附件2
             * 5、钱不够用的时候,只买前i-1件商品
             */
            for (int i = 1; i <= M; i++) {
                for (int j = 10; j <= N; j +=10) {
                    if (j >= V[i][0]) {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - V[i][0]] + V[i][0] * P[i][0]);
                    } else if (j >= V[i][0] + V[i][1]) {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - V[i][0] - V[i][1]] + V[i][0] * P[i][0] + V[i][1] * P[i][1]);
                    } else if (j >= V[i][0] + V[i][2]) {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - V[i][0] - V[i][2]] + V[i][0] * P[i][0] + V[i][2] * P[i][2]);
                    } else if (j >= V[i][0] + V[i][1] + V[i][2]) {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - V[i][0] - V[i][1] - V[i][2]] + V[i][0] * P[i][0] + V[i][1] * P[i][1] + V[i][2] * P[i][2]);
                    } else {
                        dp[i][j] = dp[i - 1][j];
                    }
                }
            }
            System.out.println(dp[M][N]);
        }
    }
}

不知道为什么通过率只有20%,总感觉牛客的测试数据有问题。

3、将二维数组优化为一维数组加状态方程:

import java.util.Scanner;

public class Main3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextInt()) {
            //总钱数
            int N = sc.nextInt();

            //总物品数
            int M = sc.nextInt();

            //二维数组,第i行表示第i个物品的价值,列:0表示主键,1表示附件1,2表示附件2
            int[][] V = new int[M+1][3];

            //二维数组,第i行表示第i个物品的重要程度,列:0表示主键,1表示附件1,2表示附件2
            int[][] P = new int[M+1][3];

            for (int i = 1; i <= M; i++) {
                int v = sc.nextInt();
                int p = sc.nextInt();
                int q = sc.nextInt();

                if (q == 0) {
                    V[i][0] = v;
                    P[i][0] = p;
                } else {
                    if (V[q][1]==0) {
                        V[q][1] = v;
                        P[q][1] = p;
                    }
                    else {
                        V[q][2] = v;
                        P[q][2] = p;
                    }
                }
            }

            //记录最大价值
            int[] dp = new int[N+1];

            /**
             * 1、钱够用的时候,买不买主键
             * 2、钱够用的时候,买不买主键、附件1
             * 3、钱够用的时候,买不买主键、附件2
             * 4、钱够用的时候,买不买主键、附件1、附件2
             * 5、钱不够用的时候,只买前i-1件商品
             */
            for (int i = 1; i <= M; i++)
                for (int j = N; j >= 10; j-=10) {
                    if (j >= V[i][0])
                        dp[j] = Math.max(dp[j], dp[j-V[i][0]]+V[i][0]*P[i][0]);
                    if (j >= V[i][0]+V[i][1])
                        dp[j] = Math.max(dp[j], dp[j-V[i][0]-V[i][1]]+V[i][0]*P[i][0]+V[i][1]*P[i][1]);
                    if (j >= V[i][0]+V[i][2])
                        dp[j] = Math.max(dp[j], dp[j-V[i][0]-V[i][2]]+V[i][0]*P[i][0]+V[i][2]*P[i][2]);
                    if (j >= V[i][0]+V[i][1]+V[i][2])
                        dp[j] = Math.max(dp[j], dp[j-V[i][0]-V[i][1]-V[i][2]]+V[i][0]*P[i][0]+V[i][1]*P[i][1]+V[i][2]*P[i][2]);
                }
            System.out.println(dp[N]);
        }
    }
}

优化为一维数组后,通过率为100%,所以还是不清楚问题在哪儿,思路相同,一个对了,一个不对。

发布了716 篇原创文章 · 获赞 130 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42764468/article/details/105422550