正整数分组
将一堆正整数分为2组,要求2组的和相差最小。
例如:1 2 3 4 5,将1 2 4分为1组,3 5分为1组,两组和相差1,是所有方案中相差最少的。 整数个数n<=100,所有整数的和<=10000
输入
第1行:一个数N,N为正整数的数量。 第2 - N+1行,N个正整数。 (N <= 100, 所有正整数的和 <= 10000)
输出
输出这个最小差
输入示例
5 1 2 3 4 5
输出示例
1
分析(转摘):
考虑最后分组情况,如果所有数的和为sum, 较小和的那组数一定不超过 [sum / 2]。我们的目标是使得和较小组的总和尽可能大。
我们的目标是从这n个数中选出一些数,这些数的总和不超过[sum / 2]且总和尽可能大。
那我们重新定义int f(i,j)表示从前i个数中选出的数,总和不超过j的时候能得到的最大的和。
则如果不选择ai f(i-1,j) = f(i,j)
如果选择ai,则f(i,j) = f(i, j - ai) j >= ai
第二维大小是[sum / 2]
初值是f(0, x) = 0 注意含义是总和“不超过”x的时候的最大值。
递推式是
f(i,j)={f(i−1,j)(j<ai)max(f(i−1,j),f(i,j−ai)+ai)(ai<=j<=[sum/2])
代码:
一维数组(优化):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int dp[10010];
int a[110];
int main()
{
int n;
scanf("%d",&n);
int sum = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d",&a[i]);
sum += a[i];
}
memset(dp , 0 , sizeof(dp)); //初始化
for(int i = 1; i <= n; i++)
for(int j = sum/2; j >= a[i]; j--)
dp[j] = max(dp[j] , dp[j-a[i]] + a[i]);
printf("%d\n",abs(sum-2*dp[sum/2]));
return 0;
}