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