版权声明:未经博主同意,不可转载 https://blog.csdn.net/pythonbanana/article/details/84197253
题目传送门
题意:
有n种硬币,这n种硬币的价值为coin[i].val,第i种硬币的个数为coin[i].num个,问能用这些硬币支付多少不超过m的价格?
思路:
多重背包裸题,但是要进行空间和时间上的优化,否则会MLE和TLE。
空间优化:通过特定的顺序滚动数组,来降维。具体可以参考背包九讲
时间优化:一般的多重背包有3重循环,i,j,k-种类,价值,数量。这里优化掉数量那一重循环。具体细节看代码注释。
AC code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i = a;i <= b;++i)
#define rep(i,a,b) for(int i = a;i >= b;--i)
const int maxn = 1e5;
int n = 0,m = 0;
struct node{
int val,num;
};
node coin[maxn+10];
bool dp[maxn+10];//不能用二维数组,那样会MLE,要滚动降维
int cnt[maxn+10];
bool cmp(node a,node b){
return a.val != b.val ? a.val < b.val : a.num < b.num;
}
void solve(){
fill(dp,dp+(maxn+10),false);
dp[0] = true;
per(i,1,n){
fill(cnt,cnt+maxn+10,0);
per(j,0,m){//正序,因为是从更新后的状态退出后面的j的状态的
if(dp[j] == true){//不要跳过,否则cnt会+1,导致过快选择的i种卡超过数量限制
continue;//为了保证使用最少的第i种卡,凑出j
}
if(j >= coin[i].val && dp[j-coin[i].val] &&
cnt[j - coin[i].val] < coin[i].num){
dp[j] = dp[j - coin[i].val];
cnt[j] = cnt[j-coin[i].val] + 1;
}
}
}
int ans = 0;
per(i,1,m){
ans += dp[i] ? 1 : 0;
}
printf("%d\n",ans);
}
int main(){
while(~scanf("%d %d",&n,&m) && ( m +n )){
per(i,1,n){
scanf("%d",&coin[i].val);
}
per(i,1,n){
scanf("%d",&coin[i].num);
}
//sort(coin+1,coin+1+n,cmp);//排序是多余的
solve();
}
return 0;
}