题解 | #购物单#
购物单
https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int m = sc.nextInt();
//1.接收数据,创建m商品;
Goods[] goods = new Goods[m + 1];
goods[0] = new Goods(0, 0, 0);
//因为题目的flag,商品编号从1开始,故goods下标也从1开始,故要加1.
int i = 1;
while (sc.hasNext()) {
goods[i++] = new Goods(sc.nextInt(), sc.nextInt(), sc.nextInt());
}
//2.根据flag标号,将附件依附在主件上;
for (int j = 1; j < m + 1; j++) {
if (goods[j].flag>0){
if (goods[goods[j].flag].a1 == 0) //若当前主件仍未分配附件则添加该附件为附件1
goods[goods[j].flag].setA1(j);
else if (goods[goods[j].flag].a2 == 0) //每个附件只能被加一次,一个主件最多可添加两个附件
goods[goods[j].flag].setA2(j);
}
}
/* for (Goods good : goods) {
if(good.flag==0)
System.out.println(good);
}*/
//对dp数组初始化 :dp[k][j]定义:手中有j元时,任买前0-i中的几件物品达到的最大满意度
int[][] dp = new int[m + 1][N + 1]; //因为商品价格都是10的倍数,所以这里总钱数也可考虑成10的倍数.3600/10=360,0-359;故加1
//不用考虑个位数非0,多出的个位数啥也买不起.对是否买得起不影响
//当有0元时,啥也买不到,故所有j=0时dp为0;
//对于k=0时0号商品价格和重要度都为0,默认0号为主件,故dp也为0;
for (int k = 1; k < m + 1; k++) { //其实只用算购买主件的几种情况,附件和主件依附着卖.
int price = 0, price1 = 0, price2 = 0, price3 = 0;
int weight = 0, weight1 = 0, weight2 = 0, weight3 = 0;
//只买主件
price = goods[k].price;
weight = goods[k].weight * price;
//主件+附件1
if (goods[k].a1 > 0) {
price1 = goods[goods[k].a1].price + price;
weight1 = goods[goods[k].a1].weight * goods[goods[k].a1].price + weight;
}
//主件+附件2
if (goods[k].a2 > 0) {
price2 = goods[goods[k].a2].price + price;
weight2 = goods[goods[k].a2].weight * goods[goods[k].a2].price + weight;
}
//主件+附件1,2
if (goods[k].a1 > 0 && goods[k].a2 > 0) {
price3 = goods[goods[k].a1].price + goods[goods[k].a2].price + price;
weight3 = goods[goods[k].a1].weight * goods[goods[k].a1].price + goods[goods[k].a2].weight * goods[goods[k].a2].price + weight;
}
for (int j = 1; j <= N ; j++) {
if (goods[k].flag > 0) dp[k][j] = dp[k - 1][j]; //当该物品为附件时不买,
else {
dp[k][j] = dp[k - 1][j]; //问题出现在这儿,如果不直接赋给dp[k][j],而是在4个if里用dp[k-1][j]判断会出现有一个用例出错
//这样可以直接在5种情况中选出最大的;
if (j >= price &&price != 0) //price初始化时为0,肯定j>=price
dp[k][j] = Math.max(dp[k][j], dp[k - 1][j-price ] + weight );
if (j >= price1&&price1 != 0)
dp[k][j] = Math.max(dp[k][j], dp[k - 1][j-price1] + weight1);
if (j >= price2&&price2 != 0)
dp[k][j] = Math.max(dp[k][j], dp[k - 1][j-price2] + weight2);
if (j >= price3&&price3 != 0)
dp[k][j] = Math.max(dp[k][j], dp[k - 1][j-price3] + weight3);
/*
* 错误代码,这里4个if之间没有进行比较,并且缺少和不买i时的比较,
* 因为就算主件有附件,也可以连主件都不买
* 故在该物品为主件时应当有5种情况:
* 1、不买;2、只买主件;3、买主件加附件1;4、买主件加附件2;5、买主件加附件1和附件2
* 在以上几种情况中选择最大的。
* eg:当买主件加附件1时,用dp[k-1][j-price1],j-price1是指当钱减去主件和附件1时的总钱数
* 这时如果买,则刚好够买。
* if (j >= price &&price != 0) //price初始化时为0,肯定j>=price
dp[k][j] = Math.max(dp[k-1][j], dp[k - 1][j-price ] + weight );
if (j >= price1&&price1 != 0)
dp[k][j] = Math.max(dp[k-1][j], dp[k - 1][j-price1] + weight1);
if (j >= price2&&price2 != 0)
dp[k][j] = Math.max(dp[k-1][j], dp[k - 1][j-price2] + weight2);
if (j >= price3&&price3 != 0)
dp[k][j] = Math.max(dp[k-1][j], dp[k - 1][j-price3] + weight3);
* */
}
}
}
System.out.println(dp[m][N]);
}
}
class Goods {
int price;//价格
int weight;//重要度
int flag;//是否为附件
int a1 = 0, a2 = 0; //附件编号,为0说明没有附件
public Goods(int price, int weight, int flag) {
this.price = price;
this.weight = weight;
this.flag = flag;
}
@Override
public String toString() {
return "Goods{" +
"price=" + price +
", weight=" + weight +
", flag=" + flag +
", a1=" + a1 +
", a2=" + a2 +
'}';
}
public void setA1(int a1) {
this.a1 = a1;
}
public void setA2(int a2) {
this.a2 = a2;
}
}


