编程之美-数组分割问题-迭代交换法

  问题来源:《编程之美》2.18数组分割 详情见Page(202-204)

  问题描述: 有一无序,个数为2n的正整数数组,要求将其拆分为元素个数为n的两个数组,并使两个子数组的和最接近。

  个人解法:两个子数组和最接近,可以有多种转化方式。比较典型的有,其中一个子数组的和最接近原数组和的二分之一;两个子数组的和差值的绝对值最小等等。

  一个比较直观的思路是首先将原数组排序,然后按照“首尾依次合并”的原则建立两个子数组,如原数组排序后为{1,2,3,4,5,6,7,8,9,10},拆分得到的子数组分别为{1,10,3,8,5}和{2,9,4,7,6}。很遗憾,这种方法并不能保证两个子数组和最接近。比如原数组排序后为{1,2,3,4,5,6,7,100},按照“首尾依次合并”的原则得到两个子数组为{1,100,3,6}和{2,7,4,5},但真正的最优解应当是{1,2,3,100}和{4,5,6,7}。很明显“首尾依次合并”的办法只能解决原数组是等差数列排布的特殊情况。虽然这种方法并不能解决问题,但可以将其作为一步预处理,尽可能简化后续的搜索过程。

  设原数组各个元素之和为sum,预处理后的两个子数组各元素之和分别为left_sum和right_sum。如果left_sum和right_sum相等,则当前的划分方案即为最优解;若left_sum和right_sum一大一小,则可能需要进行调整,若当前方案不是最优解,则需要对两个数组中的部分元素进行对换。

  简单起见,从单个元素的互换进行分析,多个元素互换可以看做单一元素互换的分步进行,最终得到的结果是一样的。元素对换的目标应当是使新得到的数组和更为接近。直观地讲就是,从和较大的数组中选取一个较大的元素,不妨设其为big,从和较小的数组中选取一个较小的数,不妨设其为small,其中big>small,将big和small互换,设互换前两数组之差的绝对值为target,元素互换后,新的数组和差值的绝对值应当减小,最理想的结果为0。若要达成此目标,则只有当0<big-small<target时才能进行互换。

  证明过程非常简单(此处不妨设left_sum>right_sum):

   |left_sum-right_sum|=left_sum-right_sum =target;

   |(left_sum-big+small)-(right_sum-small+big)|

  =  |left_sum-right_sum-2(big-small)|

  =  | target -2(big-small)|

  =  targetnew

   targetnew<target,则0<big-small<target

  此处选取互换前和较大的数组进行分析。最优的互换结果应当是big-small=target/2,此时新得到的数组和完全相等。遍历当前和较大的数组中的所有元素,分别与和较小的数组中的元素做差(实际操作时只需要与部分元素做差即可,因为有big>small的限定),设其差值为delta,则| delt-tagert/2|的值越小,表明当前互换方案更优。选出最优的互换方案后,会得到两个和更接近的新数组。之后按照上述的方法继续迭代求解,直至遍历所有元素都无法找到满足0<big-small<target的方案时停止,此时即得到最优分配方案。

  由于之前已经对数组进行过排序的预处理,根据0<big-small<target的条件,可以很快地在和较小的数组中定位搜索的开始和终止位置,从而优化算法的时间复杂度。

下面是基于VC6.0IDE的C++实现代码。在下非计算机专业出身,代码中多有不规范之处,恳请斧正,在此谢过。

#include<iostream>
using  namespace std;
#define LENGTH 10
#define LEFT 0
#define RIGHT 1
#define INF  65535
void quick_sort(int* pt,int input_lenght);
struct my_struct
{
int my_array[LENGTH];
int my_attr[LENGTH];
};
void main(void)
{
int test[LENGTH]={1,5,7,8,9,6,3,11,20,17};
quick_sort(test,LENGTH);

int left_sum=0;
int right_sum=0;
int delt=0;
int bigger=-1;//0 left bigger   1 right bigger
int tagert;
int current_dis;
int best_dis=INF;
int is_find=0;//1 找到 

int change_big_index;
int change_small_index;
int change_count=0;

int i;
int j;

int little=0;
int large=LENGTH-1;
my_struct sorted_array;
for ( i=0;i<LENGTH;i++)
{
sorted_array.my_array[i]=test[i];
cout<<test[i]<<endl;
}

//数组分划
while((large-little+1)>=4)
{
       sorted_array.my_attr[little]=LEFT;
  sorted_array.my_attr[little+1]=RIGHT;
  sorted_array.my_attr[large]=LEFT;
  sorted_array.my_attr[large-1]=RIGHT;

  little=little+2;
  large=large-2;

}
if ((large-little+1)==2)
{
  sorted_array.my_attr[little]=LEFT;
  sorted_array.my_attr[large]=RIGHT;
         
}
   

cout<<“左侧数组”<<endl;
for (i=0;i<LENGTH;i++)
{
if (sorted_array.my_attr[i]==LEFT)
{
cout<<sorted_array.my_array[i]<<endl;
}

}
    cout<<“右侧数组”<<endl;
for (i=0;i<LENGTH;i++)
{
if (sorted_array.my_attr[i]==RIGHT)
{
  
          cout<<sorted_array.my_array[i]<<endl;
}

}

   while(1)
 { 

//迭代操作开始
    is_find=0;

left_sum=0;
right_sum=0;
for (i=0;i<LENGTH;i++)
{
if (sorted_array.my_attr[i]==LEFT)
{
           left_sum=left_sum+sorted_array.my_array[i];
}
else
{
if (sorted_array.my_attr[i]==RIGHT)
{
right_sum=right_sum+sorted_array.my_array[i];
}
else
{
cout<<“出现既不属于左不属于右的元素”<<endl;
}
}
}
if (left_sum>right_sum)
{
tagert=left_sum-right_sum;
bigger=LEFT;
}
else
{
if (right_sum>left_sum)
{
tagert=right_sum-left_sum;
bigger=RIGHT;
}
else //相等
{
           cout<<“出现相等情况的完美情况”<<endl;
  break;
}
}

tagert=tagert/2;//存在相等怎么处理?
 

     for (i=0;i<LENGTH;i++)
     {
if (sorted_array.my_attr[i]==bigger)
{
 for (j=i-1;j>=0;j–)
 {
 if (sorted_array.my_attr[j]!=bigger)
 {
 delt=sorted_array.my_array[i]-sorted_array.my_array[j];
 current_dis=abs(delt-tagert);
 if (current_dis>tagert)
 {
 break;
 }
 else
 {
 //更新
 is_find=1;
 
 if (current_dis<best_dis)
 {
 best_dis=current_dis;
  change_big_index=i;
 change_small_index=j;
 }
 }

 }//结束单个元素的求差操作
 }//一个元素遍历完毕
}//end if
     }//end for

if (is_find==1) //找不到就可以结束了
{
 if (bigger==LEFT)
 {
 sorted_array.my_attr[change_big_index]=RIGHT;
 sorted_array.my_attr[change_small_index]=LEFT;
 }
 else
 {
 if (bigger==RIGHT)
 {
 sorted_array.my_attr[change_big_index]=LEFT;
 sorted_array.my_attr[change_small_index]=RIGHT;
 }
 else//直接相等 不可能
 {
                cout<<“严重错误”<<endl;
 }
 

 }
          change_count++;
 cout<<“第”<<change_count<<“次调整位置”<<endl<<endl;
 //输出看下
 cout<<“左侧数组”<<endl;
 for (i=0;i<LENGTH;i++)
 {
 if (sorted_array.my_attr[i]==LEFT)
 {
 cout<<sorted_array.my_array[i]<<endl;
 }
 
 }
 cout<<“右侧数组”<<endl;
 for (i=0;i<LENGTH;i++)
 {
 if (sorted_array.my_attr[i]==RIGHT)
 {
 
 cout<<sorted_array.my_array[i]<<endl;
 }
 
}//输出完毕
 cout<<endl<<endl<<endl;
 
}  //至此所有基本操作完成
     
if (is_find==0)//没找到
{
break;
}

 }//end while(1)
   

}//end main

void quick_sort(int* pt,int input_lenght)
{
  int my_length=input_lenght;
  int* my_array=pt;
  int key;
  int i;//small
  int j;//big
  char is_end=0;
  if (my_length==1)
  {
 return ;
  }
  i=0;
  j=my_length-1;
  key=my_array[i];
  while(1)
  {
     for (;j>i;j–)
     {
if(my_array[j]<key)
{
my_array[i]=my_array[j];
my_array[j]=key;
break;
}
     }
//判断一下 是否i和j相等
if (i==j)
{
break;
}

for (;i<j;i++)
{
if (my_array[i]>key)
{
my_array[j]=my_array[i];
my_array[i]=key;
break;

}
}
if (i==j)
{
break;
}

  }

  //计算新的首尾
  if (i>0)
  {
quick_sort(my_array,i);
  }
  
  if (j<(my_length-1))
  {
  quick_sort(my_array+j+1,my_length-i-1);
  }
 

}

    原文作者:孔纪尧
    原文地址: https://blog.csdn.net/u012895892/article/details/44999515
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞