将大小为2N的数组分成两个大小为N的数组,使得两个子数组之和最接近
#include<iostream>
using namespace std;
#define min(a,b) (((a)<=(b))?(a):(b))
void function(int arr[],int n)
{
int sum=0;
int i;
int rem;
int **isOK;
int k;
int v;
for (i=1;i<=2*n;i++)
{
sum += arr[i];
}
rem=sum/2;
isOK=(int **)malloc(sizeof(int*)*(n+3));
for (i=0;i<=n+1;i++)
{
isOK[i]=(int *)malloc(sizeof(int)*(sum+2));
memset(isOK[i],0,sizeof(isOK[i])*(sum+2));//动态分配内存之后初始化。
}
isOK[0][0]=1;
for (k=1;k<=2*n;k++)
{//对于前K个数的情况
for (i=1;i=min(k,n);i++)
//这里FOR循环I必须是递增,不能是递减,因为否则下面的isOK[i-1][v-arr[k]]会导致某个i会在前一个i-1中继续加上arr[k],导致arr[k]被使用两次
{
//这一层循环的目的在于对于正在考虑的元素arr[k],它加入进来后,该子集的个数是不确定的,因此需要逐步遍历考虑。可能前k个全部加入,也可能只是1和k两个元素。
//从前K个数里面,选择i个数。当k>n时,表示考虑到了后面n个,但是最多只能取n个。这里考虑的是从2n中取出n个,并且使得这n个之和最接近sum/2
for (v=1;v<=rem;v++)
{
if (v>=arr[k]&&(isOK[i-1][v-arr[k]]))
//这里的i表示有多少个元素
当前只考虑是否把第k个元素是否加入进来
{
isOK[i][v]=1;
}
}
}
}
v=rem;
while (v>=1&&(!isOK[n][v]))
{
v--;
}
cout<<"两个数组的差为:"<<sum-2*v<<endl;
}
int main()
{
int n;
int *arr;
int i;
cout<<"请输入 n : ";
cin>>n;
arr=(int *)malloc(sizeof(int)*(n+2));
cout<<"请输入 "<<2*n<<"个数字:"<<endl;
for (i=1;i<=2*n;i++)
{
cin>>arr[i];
}
function(arr,n);
return 0;
}