AOJ-743-多重部分和问题
Description
有n种不同大小的数字,每种各个。判断是否可以从这些数字之中选出若干使它们的和恰好为K。
Input
首先是一个正整数T(1<=T<=100)
接下来是T组数据
每组数据第一行是一个正整数n(1<=n<=100),表示有n种不同大小的数字
第二行是n个不同大小的正整数ai(1<=ai<=100000)
第三行是n个正整数mi(1<=mi<=100000),表示每种数字有mi个
第四行是一个正整数K(1<=K<=100000)
Output
对于每组数据,如果能从这些数字中选出若干使它们的和恰好为K,则输出“Yes”,否则输出“No”,每个输出单独占一行
Sample Input
2
3
3 5 8
3 2 2
17
2
1 2
1 1
4
Sample Output
Yes
No
Source
安徽省2015年“京胜杯”大学生程序设计竞赛
—————————并不华丽的分界线————————————
很明显,这是一个入门的多重背包问题。
对于背包问题,想要了解的更多的可以去看看背包九讲,或者是2009年国家集训队论文《浅谈几类背包问题》(作者是浙江省温州中学徐持衡)
对于这个背包问题,很明显只需要使用普通的解法就可以了,也就是把每种物品的数量分成1,2,4,8…..V[i] - 2^n -1(V[i]表示第i个物品的数量,其价值为A[i])就可以了。
在做多重背包时,第一个想法必然是把多重背包转化为01背包,但是这个时候,它的时间复杂度就会比较高,可能能达到O(NM∑Vi),那么这个算法就不是很可取了,但是如果考虑把V[i]化成V[i] = 1 + 2 + 4 + …. + V[i]-2^n-1且每个物品的价值也以此来改变,那么其时间复杂度可以大大优化。当然,如果想要获得更大的优化,可以使用单调队列优化。
所以这道题就非常容易了,代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100010;
int a[maxn];
int m[maxn];
int f[maxn];
bool judge(int n, int k){
for(int i = 0; i <= k; i++){
f[i] = 0;
}
for(int i = 0; i < n; i++){
int t = 1;
while(m[i] > 0){
if(t > m[i]){
t = m[i];
}
m[i] = m[i] - t;
for(int j = k; j >= a[i] * t; j--){
f[j] = max(f[j], f[j - t * a[i]] + t * a[i]);
}
t *= 2;
}
}
for(int j = 0; j <= k; j++){
if(f[j] == k)
return true;
}
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n, k;
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%d",&a[i]);
}
for(int i = 0; i < n; i++){
scanf("%d",&m[i]);
}
scanf("%d",&k);
if(judge(n, k)){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}
本人渣渣,如有哪里错误,请各位大牛指正。