把给定的 40 40 40 个数分成两组,定义权值为组内所有元素的和,求两组权值的积最大是多少。
先用背包 DP 求出两组的最优解(贪心的想法,当每组权值接近 40 40 40 个数的和的一半,积就最大),再求出乘积。
d p j dp_j dpj 为第一组的权值能否为 j j j。所以遍历 a a a 的每个元素,遍历 a i − 1 a_i-1 ai−1 致 40 40 40 个数的和的一半,若发现 d p j − a i dp_{j-a_i} dpj−ai 成立,则 d p j dp_j dpj 成立。因为 d p j dp_j dpj 可以通过 d p j − a j dp_{j-a_j} dpj−aj 增加 a j a_j aj 得到。
#include
using namespace std;
int a[]={5160,9191,6410,4657,7492,1531,8854,1253,4520,9231,
1266,4801,3484,4323,5070,1789,2744,5959,9426,4433,
4404,5291,2470,8533,7608,2935,8922,5273,8364,8819,
7374,8077,5336,8495,5602,6553,3548,5267,9150,3309};
bool dp[1000001];
long long sum,maxn,p;
int main(){
//计算40个数的和的一半
for(int i=0;i<40;i++)
sum+=a[i];
sum/=2;
dp[0]=1;
//DP
for(int i=0;i<40;i++)//遍历a的每个元素
for(int j=sum;j>=a[i];j--)
if(dp[j-a[i]]) dp[j]=1;//若发现 $dp_{j-a_i}$ 成立,则 $dp_j$ 成立
for(int i=sum;i>=0;i--)//贪心,离40个数的和的一半越近,乘积越大
if(dp[i]){
//发现最优解就赋值和跳出
maxn=i;
break;
}
p=maxn*(sum*2-maxn);//计算乘积
cout<<p;//输出
return 0;//完结散花
}
a = [5160, 9191, 6410, 4657, 7492, 1531, 8854, 1253, 4520, 9231,
1266, 4801, 3484, 4323, 5070, 1789, 2744, 5959, 9426, 4433,
4404, 5291, 2470, 8533, 7608, 2935, 8922, 5273, 8364, 8819,
7374, 8077, 5336, 8495, 5602, 6553, 3548, 5267, 9150, 3309]
sum_total = sum(a)
dp = [False] * (sum_total + 1)
dp[0] = True
for num in a:
for j in range(sum_total, num - 1, -1):
if dp[j - num]:
dp[j] = True
maxn = 0
for i in range(sum_total, -1, -1):
if dp[i]:
maxn = i
break
p = maxn * (sum_total * 2 - maxn)
print(p)
给个赞再走呗。
原文链接