题目大意:
一个人有若干不同面值、同面值不同数量的硬币。告诉你硬币面值的种类和对应的数量,求用这些硬币能正好凑成多少个小于m的整数。
解题思路:
类似于素数打表,开一个m大的bool数组记录哪些数能被凑出来,外层for循环硬币的种类,对于每种硬币都把m挨个算一遍,时间复杂度为O(n*m=1000w)。在算的时候首先要判断当前的点有没有已经能被凑出来了,如果值得去凑,那么再看j-A[i]那个点是不是已经是1了,如果已经是1了,还要判断之前那个是1的点用了几次当前种类的硬币,要保证当前面值的硬币还没用完,如果条件都满足,那么就可以把当前的点更新了。
感想:
我一开始就想这么做,公式都推出来了,但是脑子短路以为这是个n*m*c=100亿的算法(我sb的想成了对于每个n的每个m点都要把已经置1的点往后挨个+c[i]来更新,忘了可以直接倒着来,如果当前的还没更新再去更新,就是这一个细节啊,看了一眼别人的博客,发现真有这么写的,以为是数据太水过了,仔细看看才发现是自己sb了)。
Sample Input
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
Sample Output
8 4
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <stack>
using namespace std;
typedef long long LL;
const int INF=0x7fffffff;
const int MAX_N=100005;
int n,m;
int A[105];
int C[105];
bool vis[MAX_N];
int num_of_used[MAX_N];
int main(){
while(scanf("%d%d",&n,&m)&&n!=0){
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&C[i]);
}
memset(vis,0,sizeof(vis));
vis[0]=1;
int ans=0;
for(int i=1;i<=n;i++){
memset(num_of_used,0,sizeof(num_of_used));
for(int j=A[i];j<=m;j++){
if(!vis[j]&&vis[j-A[i]]&&num_of_used[j-A[i]]<C[i]){
vis[j]=1;
num_of_used[j]=num_of_used[j-A[i]]+1;
ans++;
}
}
}
printf("%d\n",ans);
}
return 0;
}