zcmu 1919: kirito's 星爆氣流斬

1919: kirito’s 星爆氣流斬

Time Limit: 2 Sec Memory Limit: 128 MB

Description

主角kirito是使用世界首款完全潛行遊戲“刀劍神域(Sword Art Online)”的玩家。曾經很幸運的參與過封閉測試,並買下正式版的kirito,正準備體驗遊戲的第一次正式營運。但在登入後不久,kirito發現“登出”指令竟然消失,而與此同時自稱是SAO遊戲設計者“茅場晶彥”的人說:“無法完成攻略就無法離開遊戲,只有打倒位於“艾恩葛朗特”頂樓,第100層的頭目-達成“完全攻略”纔是離開這個世界唯一的方法。並且,在遊戲內GAME OVER或是嘗試脫下NERvGear,玩家會立刻被NERvGear發出的高頻率微波破壞腦部而死亡。”唯有接受這個矛盾事實的人,才能夠存活下去。
自己也被捲入其中的kirito,在遊戲的舞臺——巨大浮游城堡“艾恩葛朗特”裏,以不與人組隊的獨行劍士身份,逐漸嶄露頭角,並獲得“黑色劍士”的稱號。kirito以完全攻略的條件——到達城堡最上層爲目標,持續進行嚴酷且漫長的冒險,在這期間他邂逅了女性細劍使——“閃光”亞絲娜,以及公會“血盟騎士團團長”希茲克利夫,他的命運也一步步產生了巨大的變化。kirito能否從遊戲裏全身而退……

由於kirito是封弊者,kirito有一個二刀流技能,可以使用星曝氣流斬,斬殺了強大的守關BOSS。

但是星曝氣流斬需要很龐大的法力值。
現在商店有N個藥品,kirito的物品欄有W的容量。
第i個藥品有重量w_i,可以恢復法力值v_i,有數量c_i個。
現在請你幫助kirito計算他可以恢復的最大法力值。

Input

第一行兩個整數N,W(1 <= N <= 300,1 <= W <= 500000 )
接下來N行,每行三個整數w_i,v_i,c_i(1 <= w_i <= 10000,1 <= v_i <= 10000, 1 <= c_i <= 500)

Output

輸出一個整數

Sample Input

3 6
2 2 5
3 3 8
1 4 1
Sample Output

9
HINT

Source

【分析】
最開始看到題目以爲只是簡單的多重dp問題,結果代碼提交時間超限了,天災啊!!!
後來經高人指點,說是要把這個多重dp問題轉化成01揹包問題,就能過了,而且還有個很高級的名字叫“二進制優化”,漲知識了;
我的理解就是把題目中的c_i即數量這一個屬性解放掉,比如本來有10瓶同一種類的藥水,你拆成藥效是原來1倍的藥水1瓶,以及藥效是原來2倍的藥水1瓶,以及藥效是原來4倍的藥水1瓶,和藥效是原來3倍的藥水1瓶;那麼拆分後的總藥效是11+21+41+31=10=1*10(藥效乘數量),跟拆分前的一樣,但是數量卻少了,這意味着寫代碼時候的遍歷次數少了,時間也少了!!!

【代碼】
這是之前直接多重揹包wa的代碼:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;

int dp[500010],w[310],v[310],c[310];
int n,m;

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int ans=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&w[i],&v[i],&c[i]);
        }
        fill(dp,dp+m+2,-1);
        dp[0]=0;
        for(int i=0;i<n;i++)
        {
            for(int j=1;j<=c[i];j++)
            {
                for(int k=m;k>=0;k--)
                {
                    if(dp[k]!=-1&&k+w[i]<=m)
                    {
                        dp[k+w[i]]=max(dp[k+w[i]],dp[k]+v[i]);//考慮到是從後往前找所以狀態方程是這個
                        ans=max(ans,dp[k+w[i]]);//因爲擔心k+w[i]=m這種情況不存在,所以上個保險
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

這是優化之後AC的代碼

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
int dp[500010],w[500010],v[500010];
int n,m,wi,vi,ci;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        fill(dp,dp+m+2,0);
        int k=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&wi,&vi,&ci);
            for(int j=1;j<=ci;j*=2)//這邊就是解放ci了,就是二進制優化
            {                      //創造一種新的藥水,藥效是原來的j倍,而且數量只有1瓶
                v[k]=j*vi;         //所以就不用再去建立數組裝數量了
                w[k]=j*wi;
                ci-=j;
                k++;
            }
            if(ci>0)//如果還有剩下的,就再額外創造一種藥水
            {
                v[k]=ci*vi;
                w[k]=ci*wi;
                k++;
            }
        }
        for(int i=0;i<k;i++)//轉化完之後就是01揹包問題了,時間複雜度比上面的多重揹包少不少呢
        {
            for(int j=m;j>=w[i];j--)
            {
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
            }
        }
        printf("%d\n",dp[m]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章