#include<iostream>
#include <algorithm>
using namespace std;
//有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。
int arr[] = { 0,1, 5, 7, 8, 9, 6, 3, 11, 20, 17 };
const int N = 5;
const int SUM = 87;
// 模仿动态规划解0-1背包问题的策略
int solve1()
{
int i, j, s;
int dp[2 * N + 1][N + 1][SUM / 2 + 2];
/*
用dp(i,j,c)来表示从前i个元素中取j个、且这j个元素之和不超过c的最佳(大)方案,在这里i>=j,c<=S
状态转移方程:
限第i个物品 不取
dp(i,j,c)=max{dp(i-1,j-1,c-a[i])+a[i],dp(i-1,j,c)}
dp(2N,N,SUM/2+1)就是题目的解。
*/
//初始化
memset(dp, 0, sizeof(dp));
for (i = 1; i <= 2 * N; ++i)
{
for (j = 1; j <= min(i, N); ++j)
{
for (s = arr[i]; s <= SUM / 2 + 1; ++s)
{
dp[i][j][s] = max(dp[i - 1][j - 1][s - arr[i]] + arr[i], dp[i - 1][j][s]);
}
}
}
//因为这为最终答案 dp[2*N][N][SUM/2+1];
i = 2 * N, j = N, s = SUM / 2 + 1;
while (i > 0)
{
if (dp[i][j][s] == dp[i - 1][j - 1][s - arr[i]] + arr[i]) //判定这个状态是由哪个状态推导出来的
{
cout << arr[i] << " "; //取中arr[i]
j--;
s -= arr[i];
}
i--;
}
cout << endl;
return dp[2 * N][N][SUM / 2 + 1];
}
int solve2()
{
int i, j, s;
int dp[N + 1][SUM / 2 + 2]; //取N+1件物品,总合不超过SUM/2+2,的最大值是多少
memset(dp, 0, sizeof(dp)); //初始状态都为0
for (i = 1; i <= 2 * N; ++i)
{
for (j = 1; j <= min(i, N); ++j)
{
for (s = SUM / 2 + 1; s >= arr[i]; --s) //01背包从大到小,可以省空间,即最外层的空间
{
dp[j][s] = max(dp[j - 1][s - arr[i]] + arr[i], dp[j][s]);
}
}
}
//要求最优解则 空间不能优化,
return dp[N][SUM / 2 + 1];
}
int solve3()
{
int i, j, s;
int isOK[N + 1][SUM / 2 + 2]; //isOK[i][v]表示是否可以找到i个数,使得它们之和等于v
memset(isOK, 0, sizeof(isOK)); //都不合法
//注意初始化
isOK[0][0] = 1; //可以,取0件物品,总合为0,是合法的
for (i = 1; i <= 2 * N; ++i)
{
for (j = 1; j <= min(i, N); ++j)
{
for (s = SUM / 2 + 1; s >= arr[i]; --s) //从大到小,数组少了一维
{
if (isOK[j - 1][s - arr[i]])
isOK[j][s] = 1;
}
}
}
for (s = SUM / 2 + 1; s >= 0; --s)
{
if (isOK[N][s])
return s;
}
//要求最优解则空间不能优化
return 0;
}
int main(void)
{
int ans1 = solve2();
cout << ans1 << endl;
return 0;
}
转载自http://www.cnblogs.com/freewater/archive/2012/08/23/2652974.html