Time Limit: 3000MS | Memory Limit: 30000K | |
Total Submissions: 28958 | Accepted: 9828 |
Description
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
Input
Output
Sample Input
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
Sample Output
8 4
Source
題目大意:
有n種面額的硬幣,分別爲a[i],每種硬幣一共有c[i]個。問可以構造出多少不同的價格。
總結&反思:
這道題做得實在是沒有思路,
總的來說是————
沒有真正理解好多重揹包問題一維dp的多態疊加性質,而這是因爲平時沒有主動使用一維dp造成的。
並且,老是被自己的固有思維限制住,總是想用“二進制優化”以及“隊列優化”
這道題有兩個閃光點,第一個就是多態疊加的應用,第二個就是下面代碼中num數組的使用
而多態疊加這個性質或者說真正理解一維dp正是這道題的突破關鍵點,第一個問題解決了,相信我肯定可以解決出第二個問題。
解題思路:
(1)對於每一種商品(硬幣),從1到m便利一遍所有價格,如果j-a[i]是可構造的,那麼jj就是可構造的,所以我們用dp來表示是否可構造。
(2)因爲每種商品有多個,j-a[i]可以是之前不同商品的疊加,也可以是當前商品若干個疊加的結果,對於前者沒有限制。而對於後者,使用當前商品的
個數必須小於當前商品的最大個數(且這個數值只在當前的i的時候使用,過時就無用了),那麼我們就可以用一個數組num來記錄當前這個價格是當前商品的幾次疊加
每進入一個新的i時就歸零一次。
下面是ac代碼:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 1000005
using namespace std;
bool dp[100002];
int num[100002];
int a[102];
int c[102];
int n,m;
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
if(!n&&!m) return 0;
int res=0;
memset(dp,false,sizeof(dp));
memset(num,0,sizeof(num));
for(int i=1;i<=n;i+=1){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i+=1){
scanf("%d",&c[i]);
}
dp[0]=true;
for(int i=1;i<=n;i+=1){
memset(num,0,sizeof(num));
for(int j=a[i];j<=m;j+=1){
if(dp[j-a[i]]&&!dp[j]&&num[j-a[i]]<c[i]){
dp[j]=true;
res+=1;
num[j]=num[j-a[i]]+1;
}
}
}
printf("%d\n",res);
}
return 0;
}