传送门:.php?pid=1171
描述:
2 10 1 20 1 3 10 1 20 2 30 1 -1
20 10 40 40
有n样物品,每样物品价值是v,件数是m。尽量把这些物品分成两堆使得两边总价值最接近。输出分得的两堆各自的价值。
思路一(多重背包):
因为题目要求要尽量平均分配,所以我们可以先将总价值sum求出,然后得出其分配的平均值为sum/2,要注意这个答案可能为小数,但是又因为sum是整数,所以最后得出的sum/2是要小于等于实际的值。将这个结果进行01,背包,可以得出其中一个宿舍所得的最大价值,而另一个宿舍的最大价值也可以相应的得到,而前者必定小于等于后者。
01背包专题可以见Here
代码一:
#include<bits/stdc++.h>
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<cnt; i++){for(int j=sum/2; j>=val[i]; j--){ //01背包dp[j]=max(dp[j], dp[j-val[i]]+val[i]);}}printf("%d %dn",sum-dp[sum/2],dp[sum/2]);}return 0;
}
思路二(母函数):
类似于HDU 1085的做法
利用母函数法来解决,因为分成两堆,而两堆中较小的一堆最大为所有物品总价值量的一半,所以母函数的组合数上下就可以设置成总价值量的一半。求出所有的组合后,可以利用贪心的思想来得到答案,因为要求两堆之差尽可能小,所以就可以从总价值量的一半开始向小的方向找,找到最大的价值量,则另一堆的价值量就是总价值量-此堆的价值量。因为组合数可能较大,这里不记录组合种数,而是用一个标记来表示该数能否组合出即可。
代码二:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
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<n;i++) {scanf("%d%d",&a[i],&b[i]);sum+=a[i]*b[i];}int mid=ceil(sum/2);//这个是作为两者的价值分界线for(i=0;i<=sum/2+10;i++) {c1[i]=0;c2[i]=0;}for(i=0;i<=b[0];i++) {c1[i*a[0]]=a[0];}for(i=1;i<n;i++){for(j=0;j<=mid;j++) {for(k=0;k*a[i]+j<=mid&&k<=b[i];k++) {c2[k*a[i]+j]+=c1[j];}}for(j=0;j<=mid;j++) {c1[j]=c2[j];c2[j]=0;}}//这上面是求到了一个关于这一系列设备达到mid之前的各种情况for(i=mid;i>=0;i--) {if(c1[i]!=0) break;}printf("%d %dn",sum-i,i);}return 0;
}
本文发布于:2024-02-05 05:49:03,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170725545863569.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |