HDU 2546 饭卡(带限制的01背包变形)

思路:有几个解法,如下

1)先拿出5块买最贵的菜,剩下的菜再进行01背包。如何证明正确性?设最贵的菜价e,次贵的菜价s,设减去5后的余额为x,会不会产生这样的情况,假设用5元买了e,余额最多能买到x-2钱的菜,那么共买到是x-2+e。而如果挑出s,并且有其他菜价组合加上e等于x呢?不知怎么证明。但是能AC,没有实现。

2)将余额-5作为背包容量,进行01背包,dp时记录下每种背包容量中所不包含的最大菜价,这个菜最后用那5元来买。同样,不知道如何证明。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 using namespace std;
 6 const int N=1005;
 7 int price[N];
 8 int raw[N];
 9 int dp[N];
10 int mon, n;
11 
12 void cal()
13 {
14     sort(price,price+n); //先排序
15     
16     for(int i=0; i<n; i++)
17     {
18         if(mon-5>=price[i])
19             for(int j=mon-5; j>=price[i]; j--)
20             {
21                 if(dp[j-price[i]]+price[i]>dp[j] )
22                 {
23                     dp[j]=dp[j-price[i]]+price[i];
24                     raw[j]=raw[j-price[i]]; //记录最大的菜价
25                 }
26                 else
27                     raw[j]=price[i];
28             }
29         else //为了防止余额连一个都买不上的情况
30             for(int j=mon; j>0; j--)
31                 raw[j]=price[i];
32     }
33     int ans=0;
34     for(int i=1; i<=mon; i++) //扫出所有可能最大的组合
35     {
36         ans=max(dp[i]+raw[i],ans);
37     }
38     cout<<mon-ans<<endl;
39 }
40 
41 
42 int main()
43 {
44     //freopen("input.txt","r",stdin);
45     while(cin>>n,n)
46     {
47         memset(raw, 0, sizeof(raw));
48         memset(dp, 0, sizeof(dp));
49         memset(price, 0, sizeof(price));
50         
51      
52          for(int i=0; i<n; i++)
53              scanf("%d", &price[i]);
54          cin>>mon;
55          if(mon<5)
56          {
57              cout<<mon<<endl;
58              continue;
59          }
60          cal();
61     }
62     return 0;
63 }
AC代码(01背包)
原文地址:https://www.cnblogs.com/xcw0754/p/4472755.html