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 }