時間限制:20000ms
單點時限:1000ms
記憶體限制:256mb
描述且說上一周的故事裡,小hi和小ho費勁心思終於拿到了茫茫多的獎券!而現在,終於到了小ho領取獎勵的時刻了!
小ho現在手上有m張獎券,而獎品區有n件獎品,分別標號為1到n,其中第i件獎品需要need(i)張獎券進行兌換,同時也只能兌換一次,為了使得辛苦得到的獎券不白白浪費,小ho給每件獎品都評了分,其中第i件獎品的評分值為value(i),表示他對這件獎品的喜好值。現在他想知道,憑藉他手上的這些獎券,可以換到哪些獎品,使得這些獎品的喜好值之和能夠最大。
每個測試點(輸入檔案)有且僅有一組測試資料。
每組測試資料的第一行為兩個正整數n和m,表示獎品的個數,以及小ho手中的獎券數。
接下來的n行描述每一行描述乙個獎品,其中第i行為兩個整數need(i)和value(i),意義如前文所述。
測試資料保證
對於100%的資料,n的值不超過500,m的值不超過10^5
對於100%的資料,need(i)不超過2*10^5, value(i)不超過10^3
輸出對於每組測試資料,輸出乙個整數ans,表示小ho可以獲得的總喜好值。
樣例輸入
5 1000
144 990
487 436
210 673
567 58
1056 897
樣例輸出
2099
這個問題是典型的動態規劃問題,要考慮兩個性質:重複子問題和無後效性。
首先來找子問題,用best(x)表示手裡有x張獎券時能夠獲得最高喜好的和,那麼我們要求的就是best(m),這裡有點像最少硬幣找零問題-動態規劃,但是這樣無法分解成同乙個子問題;因為在求解bset(x)還是會面臨n個獎品,選擇或不選擇,還是有2n
中選法。
做點改動,用best(i,x)表示前i中獎品是否選擇,所需獎券不超過x時的最優解決方案,那麼最終求解的便是best(n,m)。
要求解best(n,m),先求解best(n-1,m)。假設此時best(n-1,m)已知,那麼求best(n,m)時,只需要考慮第n個獎品要不要取了,如果取,則best(n,m)=best(n-1,m-need(n))+value(n);如果不取,那麼best(n,m)=best(n-1,m)。這樣便找到了遞推公式
best(i,j)=max,i>1
在計算時,只需要按照i從小到大計算即可。
for j=0……m
best(0,j)=0;
for i=1……n
for j=0……m
if(j編寫**測試:
#includeconst int maxn = 501;
const unsigned int maxm = 100001;
long int best[maxn][maxm];
inline long int max(long int a, long int b)
int main()
}delete need;
delete value;
std::cout << best[n ][m] << std::endl;
}
在計算best[i][j]
時用到的是best[i-1][0……j]
,這樣算來,其實best陣列只需要兩行即可,這兩行可以交替使用。
在計算狀態best(ia,ja)時,依賴狀態best(ia-1,0……ja),狀態best(ia-1,ja+1……m)不起作用。那麼在計算第二個迴圈時,是for(int j=0;j<=m;++j)
,如果把j倒過來計算,即for(int j=m;j>=1;--j)
;這樣可以只開闢一維陣列best(m)即可。
for j=0……m
best(j)=0;
for i=1……n
for j=m……1
if j>=need(i)
do best(j)=max(best(j),best(j-need(i))+value(i)
編寫**測試:
#includeinline long int max(long int a, long int b)
int main()
}std::cout << best[m] << std::endl;
delete best;
return 0;
}
題目1 : 完全揹包
時間限制:20000ms
單點時限:1000ms
記憶體限制:256mb
描述且說之前的故事裡,小hi和小ho費勁心思終於拿到了茫茫多的獎券!而現在,終於到了小ho領取獎勵的時刻了!
等等,這段故事為何似曾相識?這就要從平行宇宙理論說起了………總而言之,在另乙個宇宙中,小ho面臨的問題發生了細微的變化!
小ho現在手上有m張獎券,而獎品區有n種獎品,分別標號為1到n,其中第i種獎品需要need(i)張獎券進行兌換,並且可以兌換無數次,為了使得辛苦得到的獎券不白白浪費,小ho給每件獎品都評了分,其中第i件獎品的評分值為value(i),表示他對這件獎品的喜好值。現在他想知道,憑藉他手上的這些獎券,可以換到哪些獎品,使得這些獎品的喜好值之和能夠最大。
輸入每個測試點(輸入檔案)有且僅有一組測試資料。
每組測試資料的第一行為兩個正整數n和m,表示獎品的種數,以及小ho手中的獎券數。
接下來的n行描述每一行描述一種獎品,其中第i行為兩個整數need(i)和value(i),意義如前文所述。
測試資料保證
對於100%的資料,n的值不超過500,m的值不超過10^5
對於100%的資料,need(i)不超過2*10^5, value(i)不超過10^3
輸出對於每組測試資料,輸出乙個整數ans,表示小ho可以獲得的總喜好值。
樣例輸入
5 1000
144 990
487 436
210 673
567 58
1056 897
樣例輸出
5940
這個問題和01揹包不同的就是,每個獎品可以選擇多件。在01揹包問題中,每個獎品要麼選擇1件,要麼不選擇,所以叫做01揹包問題。和之前的分析一些樣,可以設乙個best(i,x),表示前i個獎品做出選擇之後(可以使選擇多件),總獎券不超過x時,價值最大的值。
那麼: be
st(i
,x)=
max,
wher
e0≤k
≤x/n
eed(
i)偽**如下:
for i=1……n
for x=0……m
for k=0……x/need(i)
best(i,x)=max,w
here
0≤k≤
x/ne
ed(i
) be
st(i
,x−n
eed(
i))=
max,
wher
e1≤k
≤x/n
eed(
i)在計算上面第乙個式子時,又把第二個式子中大部分重新計算了一邊。
那麼遞迴式子其實應該是這樣 be
st(i
,x)=
max
偽碼如下:
fori=1……n
for x=0……m
if(need(i)>x)
best(i,x)=best(i-1,x)
else
best(i,x)=max
最後考慮記憶體優化,在計算01揹包問題時,j(即x)是從m到0,因為01揹包問題的子問題是(i-1)種物品的基礎上來解決當前問題(i種物品)。而完全揹包問題的子問題是向i種物品的揹包中新增第i中物品。
測試**如下:
#include
inline
long
int max(long
int a, long
int b)
int main()
}std::cout
<< best[m] << std::endl;
delete best;
delete need;
delete value;
return
0;}
01揹包問題和完全揹包問題
在hihocoder上面的題目中看到的這個問題,總結一下。先看01揹包問題。01揹包問題 乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight i 價值為value i 現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?看到這個問題,可能會想到貪心演算法,但是貪心其實是不對的。例如...
01揹包問題和完全揹包問題
在hihocoder上面的題目中看到的這個問題,總結一下。先看01揹包問題。01揹包問題 乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight i 價值為value i 現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?看到這個問題,可能會想到貪心演算法,但是貪心其實是不對的。例如...
01揹包問題和完全揹包問題
01揹包問題,是用來介紹動態規劃演算法最經典的例子,網上關於01揹包問題的講解也很多,我寫這篇文章力爭做到用最簡單的方式,最少的公式把01揹包問題講解透徹。f i,j 表示在前i件物品中選擇若干件放在承重為 j 的揹包中,可以取得的最大價值。pi表示第i件物品的價值。決策 為了揹包中物品總價值最大化...