#include <iostream>
using namespace std;
//有一个没有排序,元素个数为N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。
const int N= 2;
int arr[] ={0,3,1, 11,2};
const int SUM = 17;
int splitArray2N_to_N()
{
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(int i=1;i<=2*N;i++)
{
for(int j=1;j<=min(i,N);j++)
{
for ( int k= 1; k<= SUM/2+1; ++k)//先赋值,因为如果数组中某个值大于SUM/2+1,则下一个for循环中dp[i][j][s]全,没赋值,错
{
dp[i][j][k] = dp[i-1][j][k];
}
for(int s= SUM/2+1; s>=arr[i]; 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>];其中<SUM/2>为>=SUM/2的最小整数.(如->4,7->4)
int iTotalNum=2*N;
int j=N;
int iEven= SUM % 2+ SUM/2; //如SUM为偶数,则取其一半;如SUM为奇数,则取其一半加1
int s= iEven;
cout<<"第一个数组中的元素为:";
while( iTotalNum > 0 )
{
if(dp[iTotalNum][j][s]==dp[iTotalNum-1][j-1][s-arr[iTotalNum]]+arr[iTotalNum]) //判定这个状态是由哪个状态推导出来的
{
cout<<arr[iTotalNum]<<" "; //取出arr[i]
j--;
s-=arr[iTotalNum];
}
iTotalNum--;
}
cout<<endl;
return dp[2*N][N][iEven];
}
int main(void)
{
int iSum1= splitArray2N_to_N(); //将一个长度为N的数组分割成个长度为N的数组,且两数组的和的差的绝对值最小,即和最接近
cout<<"第一个数组的和为:"<< iSum1<<endl;
system("pause");
return 0;
}