题解 | 购物单

购物单

https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4

package main

import (
	"fmt"
)

// 定义物品结构体
type Item struct {
	price int // 价格 (已 /10)
	val   int // 满意度 (原价格 * 重要度)
}

func main() {
	// 1. 读取输入
	var n, m int
	fmt.Scan(&n, &m)

	// 优化:价格都是10的倍数,把钱都除以10,减小数组空间
	n /= 10

	// 2. 数据预处理:归类主件和附件
	// mainItems 存储主件,accessories 存储对应主件的附件列表
	// 题目中物品编号从1开始,我们这里用 map 或者数组索引对应
	// 为了方便,用数组,索引 1~m
	
	// goods[i][0] 存主件
	// goods[i][1] 存附件1
	// goods[i][2] 存附件2
	goods := make([][3]*Item, m+1)

	for i := 1; i <= m; i++ {
		var v, p, q int
		fmt.Scan(&v, &p, &q)
		
		vReal := v / 10     // 真正的花费(优化后)
		satisfaction := v * p // 满意度 = 价格 * 重要度

		item := &Item{price: vReal, val: satisfaction}

		if q == 0 {
			// 是主件
			goods[i][0] = item
		} else {
			// 是附件,q 是它所属的主件ID
			if goods[q][1] == nil {
				goods[q][1] = item
			} else {
				goods[q][2] = item
			}
		}
	}

	// 3. 动态规划
	// dp[j] 表示预算为 j 时的最大满意度
	dp := make([]int, n+1)

	for i := 1; i <= m; i++ {
		// 只处理主件,遇到主件时,顺便考察它的附件组合
		// 如果 goods[i][0] 是 nil,说明这个编号 i 可能是个附件(已经被上面的 q 处理过了),或者不存在
		if goods[i][0] == nil {
			continue
		}

		mainItem := goods[i][0]
		acc1 := goods[i][1]
		acc2 := goods[i][2]

		// 0/1 背包倒序遍历
		for j := n; j >= 0; j-- {
			// 临时变量,用于比较不同组合
			// 基础情况:必须买得起主件
			if j >= mainItem.price {
				// 1. 只买主件
				v1 := mainItem.price
				w1 := mainItem.val
				dp[j] = max(dp[j], dp[j-v1]+w1)

				// 2. 主件 + 附件1
				if acc1 != nil {
					v2 := v1 + acc1.price
					w2 := w1 + acc1.val
					if j >= v2 {
						dp[j] = max(dp[j], dp[j-v2]+w2)
					}
				}

				// 3. 主件 + 附件2
				if acc2 != nil {
					v3 := v1 + acc2.price
					w3 := w1 + acc2.val
					if j >= v3 {
						dp[j] = max(dp[j], dp[j-v3]+w3)
					}
				}

				// 4. 主件 + 附件1 + 附件2
				if acc1 != nil && acc2 != nil {
					v4 := v1 + acc1.price + acc2.price
					w4 := w1 + acc1.val + acc2.val
					if j >= v4 {
						dp[j] = max(dp[j], dp[j-v4]+w4)
					}
				}
			}
		}
	}

	fmt.Println(dp[n])
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

全部评论
数据结构设计: 我使用了 goods[i][3] 这样的二维结构。 goods[i][0] 存放第 i 号物品的主件信息。 goods[i][1] 存放第 i 号主件的第一个附件。 goods[i][2] 存放第 i 号主件的第二个附件。 这样在遍历 i 时,只要判断 goods[i][0] 是否存在,就能一次性拿出这一组的所有搭配。 空间优化: 题目中 v 都是 10 的倍数。代码中 vReal := v / 10,对应的背包总容量 n /= 10。这能让 DP 数组的大小缩小 10 倍,速度和空间都更优。 互斥决策: 在内层 j 的循环中,我们针对同一个主件组尝试了 4 种方案。因为是在同一个 j 的循环里取 max,这保证了对于当前的钱 j,我们只会选择这 4 种方案里的某一种(或者不买),而不会出现“买了一个主件又买了同一个主件”的错误。
点赞 回复 分享
发布于 02-26 17:40 四川

相关推荐

01-14 16:23
广州商学院 Java
双非后端失败第N人:如果准备好了可以直接投字节,字节是最不看学历的,只要想面,大概率都能给你约面。
双非有机会进大厂吗
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务