考研机试 66.点菜问题
时间:2021/03/09
一.题目描述
北大网络实验室经常有活动需要叫外卖,但是每次叫外卖的报销经费的总额最大为C元,有N种菜可以点,经过长时间的点菜,网络实验室对于每种菜i都有一个量化的评价分数(表示这个菜可口程度),为Vi,每种菜的价格为Pi, 问如何选择各种菜,使得在报销额度范围内能使点到的菜的总评价分数最大。 注意:由于需要营养多样化,每种菜只能点一次。
输入描述
输入的第一行有两个整数C(1 <= C <= 1000)和N(1 <= N <= 100),C代表总共能够报销的额度,N>代表能点菜的数目。
接下来的N行每行包括两个在1到100之间(包括1和100)的的整数,分别表示菜的>价格和菜的评价分数
输出描述
输出只包括一行,这一行只包含一个整数,表示在报销额度范围内,所点的菜得到的最大评价分数。
题目链接
https://www.nowcoder.com/practice/b44f5be34a9143aa84c478d79401e22a?
tpId=40&tqId=21397&rp=1&ru=%2Fta%2Fkaoyan&qru=%2Fta%2Fkaoyan%2Fquestion-ranking&tab=answerKey
二.算法
题解
本题是在求给定金额下的最优值问题,由于每种菜只能购买一次,所以这是一个典型的0-1背包问题,也就是一个选与不选的问题。对于0-1背包问题的求解思路:若背包的最大容量记为c,物品有n种,每种物品都有自己的重量和价值,则我们建立一个n+1行,c+1列的数组dp,建立完数组后,我们令第零行和第零列的值为0,然后开始从第一行、第一列遍历整个dp数组,若当前容量j(也就是列数)大于等于该行所代表物体的重量,则dp[i][j]=max(dp[i-1][j],dp[i-1][j-该行多代表物体的重量] + 该行所代表物体的价值),即取选与不选中较大的那一个;若当前容量小于该行所代表物体的重量,则dp[i][j]=dp[i-1][j]。一定要注意下标,最好都从1开始。
重点
0-1背包问题
代码
import java.util.Scanner; public class Main{ public static void main(String[] args){ //读取输入 Scanner in = new Scanner(System.in); while(in.hasNext()){ int c = in.nextInt(); int n = in.nextInt(); int[] p = new int[n]; int[] v = new int[n]; for(int i = 0; i < n; i++){ p[i] = in.nextInt(); v[i] = in.nextInt(); } //求解点菜问题(根据0-1背包问题的思路) int[][] dp = new int[n + 1][c + 1]; for(int i = 0; i <= c; i++){ dp[0][i] = 0; } for(int i = 0; i <= n; i++){ dp[i][0] = 0; } for(int i = 1; i <= n; i++){ for(int j = 1; j <= c; j++){ if(j >= p[i - 1]){ dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - p[i - 1]] + v[i - 1]); }else{ dp[i][j] = dp[i - 1][j]; } } } System.out.println(dp[n][c]); } //System.out.println(dp(0, n - 1)); } //使用递归求解动态规划问题 // public static int dp(int money, int index){ // if(money > c){ // return 0; // } // if(index < 0){ // return 0; // }else{ // return Math.max(dp(money, index - 1), dp(money + p[index], index - 1) + v[index]); // } // } }
努力,向上,自律