標籤:2012山東省acm 完全背包 貪心 pick apples 枚舉
Pick applesTime Limit: 1000MS Memory limit: 165536K題目描述
Once ago, there is a mystery yard which only produces three kinds of apples. The number of each kind is infinite. A girl carrying a big bag comes into the yard. She is so surprised because she has never seen so many apples before. Each kind of apple has a size and a price to be sold. Now the little girl wants to gain more profits, but she does not know how. So she asks you for help, and tell she the most profits she can gain.
輸入
In the first line there is an integer T (T <= 50), indicates the number of test cases.
In each case, there are four lines. In the first three lines, there are two integers S and P in each line, which indicates the size (1 <= S<= 100) and the price (1 <= P <= 10000) of this kind of apple.
In the fourth line there is an integer V,(1 <= V <= 100,000,000)indicates the volume of the girl‘s bag.
輸出
For each case, first output the case number then follow the most profits she can gain.
樣本輸入
1 1
1 2
1 3
1 6
樣本輸出
Case 1: 6
提示 來源2012年"浪潮杯"山東省第三屆ACM大學生程式設計競賽
解題思路:
從昨天賽完就開始弄這道題,當時做的時候第一感覺就是完全背包,但是資料量太大了,直接完全背包肯定會逾時。賽後看解題報告才知道這題用的是大範圍貪心,小範圍完全背包,這個也好懂,看了網上AC的代碼小範圍用的是1000,但這個今天被老師證實是錯誤的,這題後台測試資料不完善,比如老師給的這組測試資料:
98 99
99 100
100 101
2000 這組測試資料用小範圍為1000的代碼測試結果為2000,但是正確答案應該是2020,雖然第一種是最優的東西,但是這裡我們要全部選擇第三種100 101的才是正確答案。所以小範圍為1000是錯誤的,這裡的小範圍應該是三種物品容量的最小公倍數,即100*100*100,用1000000就可以了。做這個題花了真不少功夫,整個上午差不多就是在runtime error 和 wrong answer中度過的。找錯找了很久。注意的是:限定的容量V要用long long類型,dp[]數組也要用 long long類型。dp[]數組不能只開1000000個,要盡量大點,因為在貪心部分只選擇最優的那種物品,但是大於1000000的那部分不一定能整除該物品的容量,也就是說可能會剩餘一部分容量加到1000000裡面一起用完全背包來做。這個就是無數次runtime error的原因所在。後來又看到一種解法,特別巧妙,沒有用到貪心和背包,就是枚舉除了最優物品之外的兩外兩種背包的選擇的個數,這裡有一個限定條件,那就是不是最優物品的物品被選擇的個數一定比最優物品的容量要小,
比如這組測試資料;
最優物品 3 6
其他 2 3 選一個 2 3 選兩個 4 6 選三個6 9 但這時候我們選2個最優物品的情況是 6 12,顯然比選三個其他物品要好,也就是說其他物品可能選的個數的情況有0 個,1個,2個。這些都是有可能的,本題背包的容量都小於100,所以兩層迴圈枚舉其他兩種物品被選擇的個數,在計算中取最大值就可以了,複雜度為0(100*100)。
第一種方法(排序做的貪心+完全背包):
#include <iostream>#include <string.h>#include <algorithm>using namespace std;const int lcm=1000000;//三種類size的最小公倍數long long dp[lcm*2+2],V;//dp數組要開的足夠大,不能只開lcm個,原因見後面注釋,還有V要用long longstruct N{ int s,p; double pri;}node[3];bool cmp(N a ,N b){ return a.pri<b.pri;}long long maxll(long long a,long long b){ return a>b?a:b;}void compack()//完全背包{ memset(dp,0,sizeof(dp)); for(int i=0;i<3;i++) for(int j=node[i].s;j<=V;j++) dp[j]=maxll(dp[j],dp[j-node[i].s]+node[i].p);}int main(){ int t;cin>>t;int c=1; while(t--) { for(int i=0;i<3;i++) { cin>>node[i].s>>node[i].p; node[i].pri=node[i].s*1.0/node[i].p; } cin>>V; if(V<=lcm)//小範圍直接完全背包 { compack(); cout<<"Case "<<c++<<": "<<dp[V]<<endl; } else { sort(node,node+3,cmp);//排序,最優的在第一個 V-=lcm;//超出1000000的部分 long long ans=0; ans=ans+(V/node[0].s*node[0].p);//貪心部分選擇第一個所獲得的價值 V=lcm+(V-V/node[0].s*node[0].s);//這裡解釋了為什麼dp要開的足夠大,不能只開lcm個,因為在貪心部分不一定能整除,有餘下的部分 compack();//完全背包 cout<<"Case "<<c++<<": "<<ans+dp[V]<<endl; } } return 0;}
第二種方法(未排序的貪心+完全背包):
#include <iostream>#include <string.h>using namespace std;const int lcm=1000000;long long dp[lcm*2+2],V;int s[5],p[5];void compack(){ memset(dp,0,sizeof(dp)); for(int i=0;i<3;i++) for(int j=s[i];j<=V;j++) { if(dp[j]<dp[j-s[i]]+p[i]) dp[j]=dp[j-s[i]]+p[i]; }}int main(){ int t;cin>>t;int c=1; while(t--) { double pr,temp=0; int id; for(int i=0;i<3;i++) { cin>>s[i]>>p[i]; pr=1.0*p[i]/s[i]; if(temp<pr) { temp=pr; id=i; } } cin>>V; if(V<=lcm) { compack(); cout<<"Case "<<c++<<": "<<dp[V]<<endl; } else { long long ans=(V-lcm)/s[id]*p[id]; V=V-(V-lcm)/s[id]*s[id]; compack(); cout<<"Case "<<c++<<": "<<ans+dp[V]<<endl; } }}
第三種方法(枚舉未排序):
#include <iostream>#include <stdio.h>using namespace std;int s[4],p[4];long long llmax(long long a,long long b){ return a>b?a:b;}int main(){ int t; cin>>t; int c=1; while(t--) { for(int i=1; i<=3; i++) cin>>s[i]>>p[i]; int V; cin>>V; int k1=1,k2,k3; for(int i=2; i<=3; i++) { if(p[i]*s[k1]>p[k1]*s[i])//判斷優先順序 k1=i; } if(k1==1){k2=2;k3=3;};//k1是最優的物品,k2,k3是誰沒有關係 if(k1==2){k2=1,k3=3;}; if(k1==3){k2=1,k3=2;}; long long ans=0; for(int i=0; i<s[k1]; i++)//枚舉 { for(int j=0; j<s[k1]; j++) { long long temp=i*s[k2]+j*s[k3]; if(temp>V) break; else { long long v=V-temp; ans=llmax(ans,v/s[k1]*p[k1]+i*p[k2]+j*p[k3]);//選最大值 } } } cout<<"Case "<<c++<<": "<<ans<<endl; } return 0;}
第四種方法(排序枚舉):
#include <iostream>#include <stdio.h>#include <algorithm>using namespace std;struct N{ int s,p; double pri;}node[4];bool cmp(N a,N b){ if(a.pri<b.pri) return true; return false;}long long llmax(long long a,long long b){ return a>b?a:b;}int main(){ int t; cin>>t; int c=1; while(t--) { for(int i=0; i<3; i++) { cin>>node[i].s>>node[i].p; node[i].pri=1.0*node[i].s/(1.0*node[i].p); } int V; cin>>V; sort(node,node+3,cmp); long long ans=0; for(int i=0; i<node[0].s; i++) { for(int j=0; j<node[0].s; j++) { long long temp=i*node[1].s+j*node[2].s; if(temp>V) break; else { long long v=V-temp; ans=llmax(ans,v/node[0].s*node[0].p+i*node[1].p+j*node[2].p); } } } cout<<"Case "<<c++<<": "<<ans<<endl; } return 0;}
[2012山東ACM省賽] Pick apples (貪心,完全背包,枚舉)