題面:
一家銀行計劃安裝一臺用於提取現金的機器。 機器能夠按要求的現金量發送適當的賬單。
機器使用正好N種不同的面額鈔票,例如D_k,k = 1,2,…,N,並且對於每種面額D_k,機器都有n_k張鈔票。
例如,
N = 3,
n_1 = 10,D_1 = 100,
n_2 = 4,D_2 = 50,
n_3 = 5,D_3 = 10
表示機器有10張面額爲100的鈔票、4張面額爲50的鈔票、5張面額爲10的鈔票。
現在需要編寫一個 ATM 的程序,可根據具體金額請求機器交付現金。
注意,這個程序計算程序得出的最大現金少於或等於可以根據設備的可用票據供應有效交付的現金
程序輸入來自標準輸入。 輸入中的每個數據集代表特定交易
其格式爲:Cash N n1 D1 n2 D2 … nN DN
其中0 <= Cash <= 100000是所請求的現金量,0 <= N <= 10是 紙幣面額的數量,0 <= nk <= 1000是Dk面額的可用紙幣的數量,1 <= Dk <= 1000,k = 1,N。 輸入中的數字之間可以自由出現空格。 輸入數據正確。(多組輸入)
對於每組數據,程序將在下一行中將結果打印到單獨一行上的標準輸出中。
sample input:
735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10
sample output:
735
630
0
0
第一個數據集指定一筆交易,其中請求的現金金額爲 735。 機器包含3種面額的紙幣:4張鈔票 125、6張鈔票 5和3張鈔票 350。 機器可以交付所需現金的確切金額。
在第二種情況下,機器的票據供應不能滿足所要求的確切現金數量。 可以交付的最大現金爲 630。 請注意,在機器中組合鈔票以匹配交付的現金有多種可能性。
在第三種情況下,機器是空的,沒有現金交付。 在第四種情況下,請求的現金金額爲 0,因此機器不交付現金
思路:
- 本題是一個多重揹包問題,一共有n種鈔票,請求現金金額爲V,輸入每張鈔票的面額 v[i] 和他的數量 c[i] ,對每一組 c[i] 進行二進制拆分,拆分後可將問題轉化爲01揹包問題
- 使用滾動數組 dp[j] = max(dp[j], dp[j-w[i]]+v[i]),在本題中 w v 其實是一個值,都是鈔票的面額。j 從V到0進行遍歷
- 最後dp[V]即爲結果
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cmath>
using namespace std;
const int N=1e6+1;
int V,n;
int c[20],v[20];
int dp[N],d[N];
int main()
{
while(scanf("%d",&V)!=EOF)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&c[i],&v[i]);
int count=0;
memset(d,0,N);
for(int i=1;i<=n;i++)
{
int temp=c[i];
for(int j=1;j<=temp;j<<=1)//二進制拆分
{
d[++count]=j*v[i];
temp-=j;
}
if(temp>0)
{
d[++count]=temp*v[i];
}
}
memset(dp,0,N);
for(int i=1;i<=count;i++)
{
for(int j=V;j>=0;j--)
{
if(j-d[i]>=0)
dp[j]=max(dp[j],dp[j-d[i]]+d[i]);
}
}
printf("%d\n",dp[V]);
}
return 0;
}