1. 程式人生 > >uva12325(Zombie's Treasure Chest/寶箱)=>多種方式列舉

uva12325(Zombie's Treasure Chest/寶箱)=>多種方式列舉

題意大概:你有一個體積為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;
}