2 10 1 20 1 3 10 1 20 2 30 1 -1
20 10 40 40
题意:有n样物品,每样物品价值是v,件数是m。尽量把这些物品分成两堆使得两边总价值最接近。输出分得的两堆各自的价值。
利用母函数法来解决,因为分成两堆,而两堆中较小的一堆最大为所有物品总价值量的一半,所以母函数的组合数上下就可以设置成总价值量的一半。求出所有的组合后,可以利用贪心的思想来得到答案,因为要求两堆之差尽可能小,所以就可以从总价值量的一半开始向小的方向找,找到最大的价值量,则另一堆的价值量就是总价值量-此堆的价值量。因为组合数可能较大,这里不记录组合种数,而是用一个标记来表示该数能否组合出即可。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 51;
bool ans[125002],tmp[125002];
int p[MAX],m[MAX],n;
void maxx(int &a,int &b){
int tmp;
if(a<b){
tmp = a;
a = b;
b = tmp;
}
}
void work(int limit){
int i,j,k;
memset(ans,0,sizeof(ans));
memset(tmp,0,sizeof(tmp));
for(i=0;i<=m[0];++i){
ans[i*p[0]] = true;
}
for(i=1;i<n;++i){
for(j=0;j<=limit;++j){
for(k=0;k<=m[i] && j+k*p[i]<=limit;++k){
if(tmp[j+k*p[i]] || ans[j]){
tmp[j+k*p[i]] = true;
}
}
}
for(j=0;j<=limit;++j){
ans[j] = tmp[j];
tmp[j] = false;
}
}
}
int main(){
//freopen("in.txt","r",stdin);
//(author : CSDN iaccepted)
int all,vl,vr,i;
while(scanf("%d",&n)!=EOF){
if(n<0)break;
all = 0;
for(i=0;i<n;++i){
scanf("%d %d",&p[i],&m[i]);
all += p[i] * m[i];
}
work((all + 1)/2);
for(i=(all + 1)/2;i>=0;--i){
if(ans[i]){
vl = i;
break;
}
}
vr = all - vl;
maxx(vl,vr);
printf("%d %d\n",vl,vr);
}
return 0;
}
HDU 1171 Big Event in HDU (母函数)
原文地址:http://blog.csdn.net/iaccepted/article/details/24651887