传送门:http://acm.split.hdu.edu.cn/showproblem.php?pid=1171
描述:
有n样物品,每样物品价值是v,件数是m。尽量把这些物品分成两堆使得两边总价值最接近。输出分得的两堆各自的价值。
思路一(多重背包):
因为题目要求要尽量平均分配,所以我们可以先将总价值sum求出,然后得出其分配的平均值为sum/2,要注意这个答案可能为小数,但是又因为sum是整数,所以最后得出的sum/2是要小于等于实际的值。将这个结果进行01,背包,可以得出其中一个宿舍所得的最大价值,而另一个宿舍的最大价值也可以相应的得到,而前者必定小于等于后者。
01背包专题可以见Here
代码一:
#include
using namespace std;
int val[5005];
int dp[255555];
int main(){
int n,sum,cnt;
while(~scanf("%d",&n),n>0){
memset(val, 0, sizeof(val));
memset(dp, 0, sizeof(dp));
sum=0;cnt=0;
int a,b;
for(int i=1; i<=n; i++){
scanf("%d%d",&a,&b);
while(b--){
val[cnt++]=a;//将价值存入数组
sum+=a;
}
}
for(int i=0; i=val[i]; j--){ //01背包
dp[j]=max(dp[j], dp[j-val[i]]+val[i]);
}
}
printf("%d %d\n",sum-dp[sum/2],dp[sum/2]);
}
return 0;
}
思路二(母函数):
类似于HDU 1085的做法
利用母函数法来解决,因为分成两堆,而两堆中较小的一堆最大为所有物品总价值量的一半,所以母函数的组合数上下就可以设置成总价值量的一半。求出所有的组合后,可以利用贪心的思想来得到答案,因为要求两堆之差尽可能小,所以就可以从总价值量的一半开始向小的方向找,找到最大的价值量,则另一堆的价值量就是总价值量-此堆的价值量。因为组合数可能较大,这里不记录组合种数,而是用一个标记来表示该数能否组合出即可。
代码二:
#include
#include
#include
#include
#include
using namespace std;
#define N 1000000
int c1[N];
int c2[N];
int a[N]; //表示价值
int b[N]; //表示个数
int main(){
int n,i,j,k;
while(scanf("%d",&n), n > 0) {
int sum=0;
for(i=0;i=0;i--) {
if(c1[i]!=0) break;
}
printf("%d %d\n",sum-i,i);
}
return 0;
}