数组分割

#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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值