uva12325(Zombie's Treasure Chest/寶箱)=>多種方式列舉
阿新 • • 發佈:2019-01-28
題意大概:你有一個體積為N的箱子和兩種數量無限的寶物。寶物1的體積為s1,價值為v1,寶物2的體積為s2,價值為v2.輸入均為32位帶符號整數。你的任務是計算最多能裝多大價值的寶物。例如n=100,s1=v1=34,s2=5,v2=3,那麼答案就為86,方法是裝2個寶物1,裝6個寶物2,。每種寶物都必須是拿非負整數個。
最容易想到的是列舉法。列舉寶物1的個數,然後多拿寶物2.這樣做的時間複雜度是O(n/s1),當n和s1相差非常懸殊時,效率非常低下。同樣,列舉寶物2的個數,道理是一樣的。
所以這個方法不奏效的條件是:s1和s2都很小,而n很大。這個時候,列舉的範圍就很大了。
幸運的是,s1和s2都很小的時候,有另外一種列舉法B:s2個寶物1和s1個寶物2的體積是一樣大的,而價值分別為s2*v1,和s1*v2,
1.如果前者比較大,那麼證明寶物1的價效比要比寶物2大。如果n=s1*s2的話,在寶物數量的選擇上,寶物2最多隻會拿s1-1個(否則的話,我完全可以將s1個寶物2換成價值更高的s2個寶物1);深層次地說,S2*V1>S1*V2,則我們可以得出寶物的數量最多為S1-1,因為如果我們選了S1件寶物2,則我們完全可以用S2件寶物1去代替,而且我們獲得價值會更大。
2.如果後者比較大的話,那麼證明寶物2的價效比要比寶物1大。如果n=s1*s2的話,在寶物數量的選擇上,寶物1最多隻會拿s2-1個(否則的話,我完全可以將s2個寶物1換成價值更高的s1個寶物2);
不管是哪種情況,列舉量都只有s1或者s2.
這樣就得到了一個比較“另類”的分類列舉法。
當n/s1比較小的時候列舉寶物1的個數,否則,列舉寶物2的個數,再否則就說明s1和s2都比較小,執行列舉法B。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int main() { int t,n,s1,v1,s2,v2; scanf("%d",&t); for(int tt=1; tt<=t; tt++) { //輸入體積為n的箱子,和寶物1的體積、價值以及寶物2的體積、價值。 scanf("%d%d%d%d%d",&n,&s1,&v1,&s2,&v2); //價效比=價/量 if(s1>s2) { swap(s1,s2); swap(v1,v2); } printf("Case #%d: ",tt); //此時s2肯定是比s1大的,但若如此n/s2都大於65536的話,說明 //不管是列舉寶物1還是寶物2的時間複雜度都是很大的! long long value=0; if(n/s2>=65536)// 當n/s1和n/s2都非常大的時候 { for(long long i=0; i<=s1; i++) value=max(value,v2*i+(n-i*s2)/s1*v1); for(long long i=0; i<=s2; i++) value=max(value,v1*i+(n-i*s1)/s2*v2); } else//因為資料交換的原因,s2要大些,所以n/s2要小些,此時列舉寶物2的個數 { for(long long i=0; s2*i<=n; i++) value=max(value,v2*i+(n-s2*i)/s1*v1); } printf("%lld\n",value); } return 0; }