已知由个正整数构成的集合
,将其划分为两个不相交的子集
和
,元素的个数分别是
和
,
和
中的元素之和分别为
和
.设计一个尽可能高效的算法,满足
最小且
最大。
分析:在本题中要求最小且
最大,因此我们首先可以想到的最简单直接的方法就是将集合A中的元素从小到大进行排序,然后将前
(向下取整)个元素划分在子集
中,剩下下的元素放入子集
中。这种方法显然是可以的。
下面我们来计算上述方法的时间复杂度,因为已知的排序算法中最低得时间复杂度为o(nlogn),因此该算法的最低时间复杂度是o(nlogn)。我们在仔细分析题目要求后该方法显然不是最优解,因为我们在将集合A中元素划分子集和
时,并不需要将集合A中元素,从小到大进行排序。我们只需要知道
的元素值,然后将集合A中小于
的元素划分到集合
中,将其余元素划分到
中即可。
我们可以发现和快速排序算法中的寻找枢轴值的方法非常相似。因为在快速排序算法中,每次排序都是将待排序元素中大于枢轴元的元素放在枢轴元的右边,小于枢轴元的元素放在枢轴元的左边(默认从小到大进行排序)。因此对于上述题目我们只需要比较枢轴元的位置与
大小关系。有三种情况:
1:当时满足条件。
2:当时对中[0....k-1]元素中重新寻找枢轴元素
3:当时在A[k+1...n-1]元素中重新寻找枢轴元素
重复上述步骤,直到为止
对于上述算法的时间复杂度为o(n)。
c语言代码实现
#include <stdio.h>
#include <stdlib.h>
#define maxsize 100
int setpartition(int A[],int n)
{
int flog=1;//flog相当于一个标记,用于判断枢轴元的位置k与n/2-1是否相等
int s1,s2;//用于记录集合A1和A2的元素和
int temp;//作为一个临时变量用于记录当前的枢轴元素值
int i;
int low,low0;//low0用于记录所需要排序元素的下界
low=low0=0;
int high,high0;//high0用于记录所需排序元素的上界
high=high0=n-1;
while(flog)//当flog==0时循环结束
{
temp=A[low];
while(low<high)
{
while(low<high&&A[high]>temp)
high--;
A[low]=A[high];
while(low<high&&A[low]<temp)
low++;
A[high]=A[low];
}
A[low]=temp;
if(low==n/2-1)
{
flog=0;//当low==n/2-1时表示已经找到了中间元素A[n/2-1],循环可以结束了
}
else if(low>n/2)
{
high0=--high;//更改上界
low=low0;//在区间[low0,high-1]中继续查找
}
else
{
low0=++low;//更改下届
high=high0;//在区间[low+1,high0]中继续查找
}
}
s1=s2=0;
for(i=0;i<n/2;i++)
{
s1=s1+A[i];
}
for(i=n/2;i<n;i++)
{
s2=s2+A[i];
}
return s2-s1;//返回|S2-S1|的值
}
int main()
{
int A[maxsize];
int n,i;
printf("please input the number of the set\n");
scanf("%d",&n);
printf("please input the set");
for(i=0;i<n;i++)
{
scanf("%d",&A[i]);
}
printf("%d",setpartition(A,n));
return 0;
}