1,问题描述:
一个数组例如 1,2,3,4,5。将该数组分为两部分使其和最接近例如3,4和1,2,5,一个和为8一个为7.
该问题起初看到时感觉没有什么思路,最后在网上看到用0-1背包来解决,感觉很巧妙。
解决思路:
求出数组的和SUM,要将数组分成最接近的两个集合,则每个集合的和与SUM/2很接近,其中一个集合可能大于SUM/2,另一个小于等于SUM/2.我们只需考虑小于等于SUM/2,剩下的元素自动成为另一部分。我们将SUM/2看成背包的容量,数组中的所有元素看成物品。这样该问题就转换成0-1背包问题了。
2,问题描述:
有两个数组A和B,要求交换两个数组的元素使和差最小。
解决思路:
有些人提出,将两个数组合并成一个数组,在采用上面的方法进行解决。其实这种方法是不对的,首先两个数组的元素可能相等,这样通过背包问题解出的结果不一定是最尤,再一个通过该方法得到的解不能保证每个数组的长度保持原数组的长度。所以该方法不满足条件:
实际方法:http://www.huomo.cn/developer/article-192f8.html
解决思路:
① 设两个数组为A和B,数组长度为N1,N2。假设数组A的元素和SumA大于等于数组B的
元素和SumB,即SumA >=Sum。(这个假设具有一般性,因为如果SumA小于SumB,
可以交换这两个数组,从而总是保证和较大的数组在前面)
② 如果SumA == SumB,显然不需要交换,和的最小差值为0。
③ 如果SumA > SumB,令和的差值Diff = SumA - SumB,这时我们的工作就是要通过交换两个数组中的
元素,不断减小Diff的值。
④ 每次从数组A和数组B中分别选择一个元素A[i]和B[j]进行交换,它们交换后有
Diff ' = SumA' - SumB'
= (SumA - A[i] + B[j]) - (SumB + A[i] - B[j])
= (SumA - SumB) - 2 * (A[i] - B[j])
= Diff - 2 * (A[i] - B[j])
要使Diff在交换后得到的Diff ' 减小,那么显然有 2 * (A[i] - B[j]) <= Diff 并且, A[i] - B[j] >0,
并且在满足这两个条件的情况下A[i] - B[j]的值越大,Diff的值收敛的越快,算法的效果就越好!
⑤ 重复④中的交换,直到找不到满足条件的数字对。
其中第④个步骤中,选择合适的A[i]和B[j]的时候,要将A的每个元素和B的每个元素匹配,需要两重循环,
时间复杂度为O(N1 * N2); 第⑤步中的外层循环的次数不好确定,但是直观上该循环的时间复杂度是
O(MAX(N1, N2)),所以总的时间复杂度O(N1 * N2 * Max(N1, N2))。 如果令N=N1=N2,即两个数组等长,
那么时间复杂度将会是O(N^3):
下面给出上述两个问题的实际代码:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAX(a,b) ((a) >=(b)? a:b)
int Temp[100][100] = {0};
/***************************problem1 split one array**********************************************/
void SplitArray(int array[], int len)
{
int i,j;
int SUM = 0;
for(i = 0; i < len; i++)
SUM += array[i];
SUM = SUM/2;
for(i = 1; i < len; i++) //the things
{
for(j = 1; j <= SUM; j++) //use array[i] in weight j
{
if(((j-array[i]) >= 0) )
{
Temp[i][j]= MAX(Temp[i-1][j-array[i]] + array[i], Temp[i-1][j]); //0-1packet
}
else
Temp[i][j] = Temp[i-1][j];
}
}
}
/*****************************problem2 swap two arrays*************************************/
int array1[10] = {0};
int array2[10] = {0};
void Init(int array[], int len) //produce arrays
{
// srand((int)time(NULL));
int i;
for(i = 0; i < len; i++)
{
array[i] = rand()%50;
}
}
void Print(int array[], int len) //print arrays
{
int i;
for(i = 0; i < len; i++)
{
printf("%d ", array[i]);
}
}
int CalculateSum(int array[], int len) //calculate sum of arrays
{
int i;
int sum = 0;
for(i = 0; i < len; i++)
{
sum = sum + array[i];
}
return sum;
}
void swap(int *a, int *b) //swap two elements
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void process(int arrayA[], int arrayB[], int len1, int len2)//main process procedure
{
int SumA, SumB;
SumA = CalculateSum(arrayA, len1);
SumB = CalculateSum(arrayB, len2);
printf("sumA is:%d\n", SumA);
printf("sumB is:%d\n", SumB);
int *Amax;
int *Amin;
int Maxdiff;
if(SumA > SumB)
{
Maxdiff = SumA - SumB;
Amax = arrayA;
Amin = arrayB;
}
else
{
Maxdiff = SumB - SumA;
Amax = arrayB;
Amin = arrayA;
}
if(SumA == SumB)
{
printf("it is already min");
printf("\n");
return ;
}
int i, j, temp;
int index1, index2;
int stopflag =0;
for(;;)
{
temp = 0;
for(i = 0; i < len1; i++)
for(j = 0; j < len2; j++)
{
if((Amax[i] -Amin [j] > 0) && (Maxdiff >2*(Amax[i]-Amin[j])))
{
if(temp < 2*(Amax[i] - Amin[j]))
{
index1 = i;
index2 = j;
temp = 2*(Amax[i] - Amin[j]);
stopflag = 1;
}
}
}
if(stopflag)
{
Maxdiff = Maxdiff - temp;
swap(&Amax[index1], &Amin[index2]);
stopflag = 0;
}
else
{
return ;
}
}
}
/******************************************************************************/
int main()
{
int array[7] = {0,1,2,3,4,5,6};
SplitArray(array, 7);
int i;
int j;
int SUM = 0;
for(i = 0; i < 7; i++)
SUM += array[i];
SUM = SUM/2;
for(i = 0; i< 7; i++)
{ for(j = 0; j <= SUM; j++)
printf("%d ", Temp[i][j]);
printf("\n");
}
int len1 = 10;
int len2 = 10;
int SumA, SumB;
srand((int)time(NULL));
Init(array1, len1);
Init(array2, len2);
printf("\n");
Print(array1, len1);
printf("\n");
Print(array2, len2);
printf("\n");
process(array1, array2, len1, len2);
Print(array1, len1);
printf("\n");
Print(array2, len2);
printf("\n");
SumA = CalculateSum(array1, len1);
SumB = CalculateSum(array2, len2);
printf("sumA is:%d\n", SumA);
printf("sumB is:%d\n", SumB);
}