书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;
}