题目概述:有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。
先给一个空间复杂度为O(2N*N*Sum/2)即O(N2Sum)的方法,下面会对空间复杂度进行优化:
/*
ArrayPartition
int data[] = {1, 5, 7, 8, 9, 6, 3, 11, 20, 17};
将一个有2n个元素的数组分割成元素个数相等(n)的两部分,使得两部分的和值最接近
@author arhaiyun
date:2013/09/24
*/
#include "stdafx.h"
#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;
/*
[1].多维数组内存的动态分配
[2].动态规划思想的应用
*/
int main(int argc, char* argv[])
{
//从文件中读取输入
// fstream input("in.txt", ios::in);
int data[] = {1, 5, 5, 7, 8, 9, 6, 3, 11, 20, 17, 6};
int i, j, k;
const int n = 6;
int sum = 0;
for(i = 0; i < 10; i++)
{
sum += data[i];
}
cout<<"sum : "<<sum<<endl;
int half = sum / 2;
int ***dp = new int**[2 * n + 1];
int ***path = new int**[2 * n + 1];
/*三维数组的初始化 空间复杂度O(sum*n^2)
* dp[i][j][k]表示从数组前面i个元素中去j个元素的和最接近或等于k时候的值
* path[i][j][k] 表示在这一步选择时候是否选择了data[i - 1];
*/
for(i = 0; i < 2 * n + 1; i++)
{
dp[i] = new int*[n + 1];
path[i] = new int*[n + 1];
for(j = 0; j < n + 1; j++)
{
dp[i][j] = new int[half + 1];
path[i][j] = new int[half + 1];
for(k = 0; k < half + 1; k++)
{
dp[i][j][k] = 0;
path[i][j][k] = 0;
}
}
}
for(i = 1; i <= 2 * n; i++)
{
//限制取元素的个数小于等于n
int limit = (i > n) ? n : i;
for(j = 1; j <= limit; j++)
{
for(k = 0; k <= half; k++)
{
//加入data[i-1] 如果有更接近k的值,将路径记录下来
if(data[i - 1] <= k && dp[i - 1][j - 1][k - data[i - 1]] + data[i - 1]<= k
&& dp[i - 1][j - 1][k - data[i - 1]] + data[i - 1] > dp[i - 1][j][k])
{
dp[i][j][k] = dp[i - 1][j - 1][k - data[i - 1]] + data[i - 1];
path[i][j][k] = 1;
}
else
dp[i][j][k] = dp[i - 1][j][k];
}
}
}
cout<<"Left sum:"<<dp[10][5][half]<<endl;
i = 10;
j = n;
k = half;
while(i > 0 && j > 0 && k > 0)
{
if(path[i][j][k] == 1)
{
cout<<data[i - 1]<<" ";
j = j - 1;
k = k - data[i - 1];
}
i = i - 1;
}
system("pause");
return 0;
}