点菜题(动态规划)

书p310,例8.3

问题描述

点菜,每次金额最大C,可点N个菜,每个菜价格pi,评价vi,每个菜只能点一次,求金额范围内所点菜品最大评分。
第一行:两个数C,N
接下来N行,每行分别代表菜的价格和评分

样例

输入
90 4
20 25
30 20
40 50
10 18
40 2(这是第二个样例了)
25 30
10 8
输出
95
38

思路

类似0/1背包,每个菜只能选或不选,且最多只能选一次
用dp[j]表示金额i时最大评分,则dp[C]即为答案,初始化全0

由于每种菜最多只能选一次,为了避免重复计算某道菜,要在外层循环菜品
对应每种菜品,不选时,dp[j]不变
如果要选择,在金额 j 时,比较更新加入后的dp[j]
如果加入比原来评分高,那就加入并更新,否则就不加入保持原状
故得转移方程
dp[j] = max( dp[j] , dp[j-p[i]] + v[i])

由于每个菜品只循环了一次,所以不会重复
对每种菜品,在每个符合条件的价格处选择是否加入,所以每个价格处都是当前最优解,循环完就是全局最优解

补充

通过昨天和别人讨论,用下面这个dp数组可能更好理解
dp[i][j]表示前i个菜金额为j时的最大评分
每多考虑一个菜,要看这个菜的性价比适不适合加进来,以及在什么金额的时候加,思路跟上面一样,考虑这个菜加还是不加
转移方程就是dp[i][j] = max( dp[i-1][j] , dp[i-1][j-p[i]] + v[i])
上一方法是对本方法在空间上的优化,因为dp[i][ ]只根据dp[i-1][ ]来改变,只用一维dp就可以了。

代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define MAXN 101
#define MAXV 1001

int N, C;
int p[MAXN];
int v[MAXN];
int dp[MAXV];

void solve() {											
	for (int i = 1; i <= N; i++) {
		for (int j = C; j >= p[i]; j--) {
			dp[j] = max(dp[j], dp[j - p[i]] + v[i]);
		}
	}
}

int main() {
	while (scanf("%d%d", &C, &N) != EOF) {
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= N; i++) {
			scanf("%d%d", &p[i], &v[i]);
		}
		solve();
		printf("%d\n",dp[C]);
	}
	return 0;
}

结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值