点菜题(动态规划)
書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 1001int 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; }結果
總結
- 上一篇: 注册gmail账号,手机无法接受验证码的
- 下一篇: [问题已处理]-centos7 hist