解法1(参照每天一算法32):
当前数组a和数组b的和之差为 A = sum(a) - sum(b) a的第i个元素和b的第j个元素交换后,a和b的和之差为 A' = sum(a) - a[i] + b[j] - (sum(b)- b[j] + a[i]) = sum(a) - sum(b) - 2 (a[i] - b[j]) = A - 2 (a[i] - b[j])
设x= a[i] - b[j] |A| - |A'| = |A| - |A-2x| 假设A> 0, 当x在(0,A)之间时,做这样的交换才能使得交换后的a和b的和之差变小,x越接近A/2效果越好, 如果找不到 在(0,A)之间的x,则当前的a和b就是答案。
所以算法大概如下: 在a和b中寻找使得x在(0,A)之间并且最接近A/2的i和j,交换相应的i和j元素,重新计算A后,重复前面的步骤直至找不到(0,A)之间的x为止。
#include <iostream>
using namespace std;
int sum(int a[],int n)
{
int sum=0;
for(int i=0;i<n;i++)
sum+=a[i];
return sum;
}
void adjust(int *a,int *b,int n)
{
bool ifCycle=true;
while(ifCycle)
{
ifCycle=false;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
int subij=a[i]-b[j];
int sub=sum(a,n)-sum(b,n);
if(subij*sub>0 && abs(subij)<abs(sub))
{
ifCycle = true;
int item = a[i];
a[i] = b[j];
b[j] = item;
}
}
}
}
}
int main()
{
int A[]={5,8,1,6,40,46};
int n=sizeof(A)/sizeof(int);
int *a=new int [n/2];
int *b=new int [n/2];
for (int i=0;i<n/2;i++) //初试分配数组
{
a[i]=A[i];
b[i]=A[n/2+i];
}
adjust(a,b,n/2);
for(int i=0;i<n/2;i++)
cout<<a[i]<<" ";
cout<<endl;
for(int i=0;i<n/2;i++)
cout<<b[i]<<" ";
cout<<endl;
system("pause");
delete []a;
delete []b;
return 0;
}
解法2:
这个问题存储的是从前k个数中选取任意个数,且其和为s的取法是否存在flag[k][s]。之所以将选出的数之和放在下标中,而不是作为dp[k]的值,是因为那种做法不满足动态规划的前提——最优化原理,假设我们找到最优解有k个数p1p2...pk(选出的这k个数之和是最接近sum/2的),但最优解的前k-1个数p1p2...pk-1之和可能并不是最接近sum/2的,也就是说可能在访问到pk之前有另一组数q1q2....qk-1其和相比p1p2...pk-1之和会更接近sum/2,即最优解的子问题并不是最优的,所以不满足最优化原理。因此我们需要将dp[k]的值作为下标存储起来,将这个最优问题转化为判定问题,用带动态规划的思想的递推法来解。
外阶段:在前k1个数中进行选择,k1=1,2...2*n。
内阶段:从这k1个数中任意选出k2个数,k2=1,2...k1。
状态:这k2个数的和为s,s=1,2...sum/2。
选出的物体数必须为n,这个条件限制了内阶段k2的取值范围,并且flag[k][s]的含义也发生变化。这里的dp[k][s]表示从前k个数中取k个数,且k不超过n,且这些数之和为s的取法是否存在。
#include <iostream>
using namespace std;
void func(int *A,int len)
{
int i,j,s=0;
int n = len / 2;
int sum = 0;
for (int i = 0; i <len ; i++) {
sum += A[i];
}
// 动态分配:flag[i][j]:任意i个数组元素之和是j,则flag[i][j]为true
bool **flag;
flag=(bool **)malloc(sizeof(bool*)*(len+1));/*创建t个行指针*/
for (i=0;i<len+1;++i)/*分别创建数组的每一行*/
flag[i]=(bool *)malloc(sizeof(bool)*(sum/2+1));
for (i = 0; i < len; i++)
for (j = 0; j < sum / 2 + 1; j++)
flag[i][j] = false;
flag[0][0] = true;
for (int k = 0; k < len; k++) { // 外阶段k表示第k个数,
for (i = k > n ? n : k; i >= 1; i--) { //内阶段i表示选取数的个数
for (s = 0; s <= sum / 2; s++) { // 状态s
if (s >= A[k] && flag[i - 1][s - A[k]]) // s >= A[k]条件是为了让flag数组的第二维不越界
{ flag[i][s] = true;
}
}
}
}
for (i = sum / 2; i >= 0; i--) { //最接近Sum/2的和
if (flag[n][i]) {
cout<<"sum is "<< sum<<endl;
cout<<"sum/2 is " << sum / 2<<endl;
cout<<"i is " <<i<<endl;
cout<<"minimum delta is " <<abs(2 * i - sum)<<endl;
s=i;
break;
}
}
for(i=0;i<len;i++) //输出取得和的len/位数组
{
for(j=len-1;j>i;j--)
{
for(int k=len-1;k>j;k--)
{
if (flag[len/2][s-A[i]] && flag[len/2-1][s-A[i]-A[j]] && s-A[i]-A[j]-A[k]==0 )
{
cout<<A[i]<<"\t"<<A[j]<<"\t"<<A[k]<<endl;
}
}
}
}
}
void main ()
{
int A[] = { 1, 2, 3, 5, 7, 8, 9 };
int length = sizeof(A)/sizeof(int);
func(A,length);
system("pause");
}