01揹包問題和完全揹包問題

2021-06-28 15:30:09 字數 4300 閱讀 9331

時間限制: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件物品的價值。決策 為了揹包中物品總價值最大化...