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;
}