HJ16_购物单

一、购物单

原题太多复制有乱码算了,贴个链接吧。
牛客

二、解题

看了题解博主思路和官方视频帮助理解。
我从学习动态规划是什么,到0-1背包问题,再到这个原题,花了两天半,流泪
就这写代码还是出错了两个地点,因为我始终没搞懂加了附件咋遍历的,反正理解不是很透彻

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HJ16_ShoppingList
{
    class Program
    {
        /*
         * 0-1-2-3背包问题
         * 分析 1.价格是10的整数倍,读入时候可以除以10
         * 2.先读取钱数与物品数量,将钱除10
         * 3.将每个物品分为主,主+附件1,主+附件2,主+附件1+附件2的情况分别枚举
         * 4.如果背包放得下,肯定东西越多价值越大,所以可依次枚举替换,
         * 没有附件的再替换价值也是原值
         * 5.价钱与价值都用二维数组存储
         * 6.结果也用二维数组存储,行长度物品+1,列长度M+1
         */
        public static void Main(string[] args)
        {
            string line;
            while ((line = System.Console.ReadLine()) != null)
            {
                // 注意 while 处理多个 case
                // 1.定义变量
                string[] tokens = line.Split(' ');
                int M = Convert.ToInt32(tokens[0]) / 10;// 总预算
                int N = Convert.ToInt32(tokens[1]); // 物品数量
                int[,] price = new int[N + 1, 3]; //价格数组
                int[,] value = new int[N + 1, 3]; // 价值数组
                int[,] dp = new int[N + 1, M + 1]; // 结果数组
                // 输出测试
                //System.Console.WriteLine(M + " " + N + "");
                // 2.依次读入价格与重量
                for (int i = 1; i <= N; i++)
                {
                    // 物品编号从1开始
                    string line1 = System.Console.ReadLine();
                    string[] tokens1 = line1.Split(' ');
                    int a = Convert.ToInt32(tokens1[0]) / 10;// 物品价格
                    int b = Convert.ToInt32(tokens1[1]); // 重要程度
                    int c = Convert.ToInt32(tokens1[2]); //主件还是附件
                    if (c == 0)
                    {
                        // 物品是主件
                        price[i, 0] = a; //价格
                        value[i, 0] = b;// 重要度
                    }
                    else
                    {
                        if (price[c, 1] > 0)
                        {
                            // c号物品已经有附件1
                            price[c, 2] = a; //价格
                            value[c, 2] = b;// 重要度
                        }
                        else
                        {
                            // c号物品的附件1的价格和重要度
                            price[c, 1] = a;//价格
                            value[c, 1] = b;// 重要度
                        }
                    }

                }
                // 3.先输出试试看吧,新需求,怎么输出一大段并添加换行符                
                //Console.WriteLine("w[i][s]数组--i代表第几个物品,s代表其附件价格,没有附件则是0");
                //for (int i = 0; i <= N; i++)
                //{
                //    for (int j = 0; j < price.GetLength(1); j++)
                //    {
                //        if (price[i, j] == 0)
                //        {
                //            Console.Write(price[i, j] + "0");
                //        }
                //        else
                //        {
                //            Console.Write(price[i, j]);
                //        }
                //        Console.Write(" ");
                //    }
                //    Console.WriteLine();
                //}
                //Console.WriteLine("v[i][s]数组--i代表第几个物品,s代表其附件价值,没有附件则是0");
                //for (int i = 0; i <= N; i++)
                //{
                //    for (int j = 0; j < value.GetLength(1); j++)
                //    {
                //        if (value[i, j] == 0)
                //        {
                //            Console.Write(value[i, j] + "0");
                //        }
                //        else
                //        {
                //            Console.Write(value[i, j]);
                //        }
                //        Console.Write(" ");
                //    }
                //    Console.WriteLine();
                //}
                // 4.读取好了,开始处理吧
                // 直接写状态转移函数与循环吧,算满意度时候记得枚举每个物品可能出现的情况
                // 假设i代表第几个物品,k代表不同情况
                // k=0:买一个主件;k=1:买一个主件和附件1;k=2:买一个主件和附件2;
                // k=3:买一个主件,附件1,附件2
                // 先按需求取出数据,一个主件的价格、价值,附件1的价格、价值
                // 附件2的价格、价值
                // 再按情况依次算
                // 官方给的答案是四种情况分别赋值dp[i,j]
                // ****假如均不超过容量,那么为什么主加附2一定可以替换主加附1?
                // ****为什么不需要取这四个中满意度最大的?
                // 记住满意度是价值*价格与状态转移方程
                // 双循环i对物品数量,j对背包容量
                for (int i = 1; i <= N; i++)
                {
                    for (int j = i; j <= M; j++)
                    {
                        // ,b是主件的满意度,
                        // 满意度是主附件 价格*价值累加和,这里直接预处理
                        // 说明:price[i, 0]是主件的价格,price[i, 1]是附件1的价格,
                        // price[i, 2]是附件2的价格
                        // 假设a是只买主件的钱,b是只买主件的满意度
                        int a = price[i, 0], b = value[i, 0] * a;
                        // 假设c是买主+附1钱,d是买主+附1满意度
                        int c = a + price[i, 1], d = b + value[i, 1] * price[i, 1];
                        // 假设e是买主加附2钱,f是买主+附2满意度
                        int e = a + price[i, 2], f = b + value[i, 2] * price[i, 2];
                        // 假设g是买主+附1+附2钱,h是买主+附1+附2满意度
                        int g = a + price[i, 1] + price[i, 2], h = b + value[i, 1] * price[i, 1] + value[i, 2] * price[i, 2];
                        // 状态转移方程
                        // 官方答案三目运算符,我不熟悉还是先用我推出来的初级公式吧
                        /*
                         * 1.测试第二个用例时候错了,发现当主件加附件超过预算时候,返回上一级
                         * 是返回同行只买主件情况,不是很懂,需要数学模型
                         * 2.提交发现三组没对,再看大佬过程发现,
                         * 凡是加了主件需要返回的,都要回到只买主件
                         * 我还奇怪能买下咋不回到主件,原来是我当时只看了第一行,
                         * 第一行就是主件肯定返回上一级,然后下一行有了附件才是回到主件
                         */
                        // a情况,只买主件
                        if (a > j)
                        {
                            // 物品价格大于预算,返回上一个物品dp[i-1,j],即表格中同列上一行
                            dp[i, j] = dp[i - 1, j];
                        }
                        else
                        {
                            // 物品价格小于预算,可以选择买或不买,分别求满意度取最大
                            // 买,满意度为买了a的满意度,加上,回到上一个物品再减去a的钱能买的最大满意度
                            int A = b + dp[i - 1, j - a];
                            // 不买,返回上个物品最大满意度
                            int B = dp[i - 1, j];
                            dp[i, j] = max(A, B);
                        }
                        // c情况,主件+附件1
                        if (c > j)
                        {
                            // 物品价格大于预算,返回上一个物品dp[i-1,j],即表格中同列上一行
                            dp[i, j] = dp[i, j];
                        }
                        else
                        {
                            // 物品价格小于预算,可以选择买或不买,分别求满意度取最大
                            // 买,满意度为买了a的满意度,加上,回到上一个物品再减去a的钱能买的最大满意度
                            int A = d + dp[i - 1, j - c];
                            // 不买,返回上个物品最大满意度
                            int B = dp[i, j];
                            dp[i, j] = max(A, B);
                        }
                        // e情况
                        if (e > j)
                        {
                            // 物品价格大于预算,返回上一个物品dp[i-1,j],即表格中同列上一行
                            dp[i, j] = dp[i, j];
                        }
                        else
                        {
                            // 物品价格小于预算,可以选择买或不买,分别求满意度取最大
                            // 买,满意度为买了a的满意度,加上,回到上一个物品再减去a的钱能买的最大满意度
                            int A = f + dp[i - 1, j - e];
                            // 不买,返回上个物品最大满意度
                            int B = dp[i, j];
                            dp[i, j] = max(A, B);
                        }
                        // g情况
                        if (g > j)
                        {
                            // 物品价格大于预算,返回上一个物品dp[i-1,j],即表格中同列上一行
                            dp[i, j] = dp[i, j];
                        }
                        else
                        {
                            // 物品价格小于预算,可以选择买或不买,分别求满意度取最大
                            // 买,满意度为买了a的满意度,加上,回到上一个物品再减去a的钱能买的最大满意度
                            int A = h + dp[i - 1, j - g];
                            // 不买,返回上个物品最大满意度
                            int B = dp[i, j];
                            dp[i, j] = max(A, B);
                        }
                        // 比较四个最大的
                        //dp[i, j] = max(a1, a2, a3, a4);
                    }
                }
                // 5.输出结果
                Console.WriteLine(dp[N, M] * 10);
            }
        }
        /// <summary>
        /// 比较大小
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static int max(int a, int b)
        {
            if (a > b)
            {
                return a;
            }
            else
            {
                return b;
            }
        }
        public static int max(int a, int b, int c, int d)
        {
            int[] arr = new int[4] { a, b, c, d };
            Array.Sort(arr);
            return arr[0];
        }
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值