洛谷 P1064 [NOIP2006 提高组] 金明的预算方案 (Java 写的)

这道题重在思路,也是典型的0 1 背包问题,是背包问题的升级版

主件携带附件,在Java中很容易就想到了map结构,key是主件的id值,value是附件

当key为0时候,里面就全是主件了

同时考虑状态转移方程,考虑的情况,一共有4种

1、买主件 + 两个附件

2、买主件 + 一个附件

3、只买主件

4、什么都不买

考虑好之后可以直接上代码了,因为人是弱鸡,只会二维背包动态,代码如下

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.*;

/**
 * @Author congge
 * @Date 2023/10/1 21:03
 * @description 金明的预算方案
 * https://www.luogu.com.cn/problem/P1064
 */
public class Main {
    public static void main(String[] args) {
        Input scan = new Input();
        int n = scan.nextInt() / 10;
        int m = scan.nextInt();
        /**
         * 保留所有主键以及附件
         * father 为 0 的都存到了0号位当中
         */
        HashMap<Integer, ArrayList<Good>> sonAndFather = new HashMap<>();

        /**
         * 初始化map
         */
        for (int i = 0; i <= m; i++) {
            sonAndFather.put(i, new ArrayList<>());
        }

        /**
         * 键入
         */
        for (int i = 1; i <= m; i++) {
            Good good = new Good();
            good.v = scan.nextInt() / 10;
            good.p = scan.nextInt() * good.v;
            good.father = scan.nextInt();
            good.idx = i;
            sonAndFather.get(good.father).add(good);
        }

        ArrayList<Good> fathers = sonAndFather.get(0);
        int M = fathers.size();

        /**
         * 这里只需要dp一下主键即可,附件附加到主键上面判断就好
         */
        int[][] dp = new int[M][n + 1];

        Good father = fathers.get(0);
        ArrayList<Good> sonOfFather = sonAndFather.get(father.idx);

        /**
         * 表示当前主键的孩子数目
         */
        int sonOfSize = sonOfFather.size();
        /**
         * 用于计算两个孩子的时候总的钱与价值
         */
        int sumV;
        int sumP;

        /**
         * 用于计算一个孩子的时候的钱与价值
         */
        int curSumV;
        int curSumP;

        /**
         * 初始化边界
         */
        for (int j = father.v; j <= n; j++) {

            /**
             * 带两个附件的
             */
            if (sonOfSize == 2) {
                sumV = father.v + sonOfFather.get(0).v + sonOfFather.get(1).v;
                /**
                 * 钱大于总共的钱的时候计算
                 */
                if (j >= sumV) {
                    sumP = father.p + sonOfFather.get(0).p + sonOfFather.get(1).p;
                    dp[0][j] = sumP;
                    continue;
                }
            }

            /**
             * 带一个附件的
             */
            if (sonOfSize >= 1) {
                for (int k = 0; k < sonOfSize; k++) {
                    curSumV = sonOfFather.get(k).v + father.v;

                    /**
                     * 这里同理
                     */
                    if (j >= curSumV) {
                        curSumP = sonOfFather.get(k).p + father.p;
                        dp[0][j] = Math.max(dp[0][j], curSumP);
                    }
                }
            }
            /**
             * 只有主件的情况
             */
            sumV = father.v;
            if (j >= sumV) {
                sumP = father.p;
                dp[0][j] = Math.max(dp[0][j], sumP);
            }
        }

        for (int i = 1; i < M; i++) {
            father = fathers.get(i);
            sonOfFather = sonAndFather.get(father.idx);
            sonOfSize = sonOfFather.size();
            
            for (int j = 1; j <= n; j++) {

                /**
                 * 带两个附件的
                 */
                if (sonOfSize == 2) {
                    sumV = father.v + sonOfFather.get(0).v + sonOfFather.get(1).v;
                    if (j >= sumV) {
                        sumP = father.p + sonOfFather.get(0).p + sonOfFather.get(1).p;
                        dp[i][j] = dp[i - 1][j - sumV] + sumP;
                    }
                }

                /**
                 * 带一个附件的
                 */
                if (sonOfSize >= 1) {
                    for (int k = 0; k < sonOfSize; k++) {
                        curSumV = sonOfFather.get(k).v + father.v;
                        if (j >= curSumV) {
                            curSumP = sonOfFather.get(k).p + father.p;
                            dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - curSumV] + curSumP);
                        }
                    }
                }

                /**
                 * 只有主件的情况
                 */
                if (j >= father.v) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - father.v] + father.p);
                }

                /**
                 * 什么都不买的时候
                 */
                dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
            }
        }

        System.out.println(dp[M - 1][n] * 10);
    }

    static class Good {
        int v;
        int p;
        int idx;
        int father;
    }

    static class Input {
        StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

        public int nextInt() {
            try {
                in.nextToken();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return (int) in.nval;
        }
    }

    
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值