整数集合划分且满足|n1-n2|最小且|S1-S2|最大


一、问题描述

  已知由n(n≥2)个正整数构成的集合A={ak}(0≤k<n),将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2, A1和A2中元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足|n1-n2|最小且|S1-S2|最大。

二、实现思路

  在划分算法中,集合A的元素都为正整数,则只需要找到中位数,以中位数为界,将小于中位数的部分归为一个集合,大于等于中位数包括中位数在内的归为另外一个集合。此时,|n1-n2|等于0(数组元素个数为奇数)或1(数组元素个数为偶数),且|S1-S2|最大。很简单的做法就是对数组进行排序,随后输出。但这样基于排序的时间复杂度至少是O(nlogn)(快速排序)。这里注意,由于只需要将数组分割,分割的集合不要求有序,所以只需找到这样一个元素(轴枢),它可以将数组一分为二:该元素之前的元素小于它,该元素之后的元素都大于等于它。如该元素的位置恰好是中央,则划分操作结束。
  算法实现具体步骤为:

  1. 选择轴枢元素,进行一趟划分,得到轴枢元素的准确位置;
  2. 将轴枢元素位置和数组中央进行比较,如轴枢元素不位于中央,递归处理左(或右)半数组,直至所得元素位置恰好位于数组中央。

  对 n+(1/2)n+(1/4)n+(1/8)n+…,取极限得到O(n),则本算法时间复杂度为O(n)。空间复杂度为O(1)。

三、解题代码

#include <iostream>

using namespace std;
#define MaxSize 20000

/*一趟划分*/
int partition(int R[], int low, int high)
{
	int temp = R[low];   //以R[low]为基准
	while (low<high)      //从两端交替向中间扫描
	{
		while (low < high&&R[high] >= temp) //逆向查找首个小于枢轴的元素
		{
			high--;
		}
		R[low] = R[high];  //将小于枢轴的元素交换到低端
		while (low < high&&R[low] <= temp)//正向查找首个大于枢轴的元素
		{
			low++;
		}
		R[high] = R[low];	//将大于枢轴的元素交换到高端
	}
	R[low] = temp; //枢轴元素置于正确的位置
	return low;	   //返回枢轴元素的位置
}

/*查找n/2,数组以n/2排序,将序列分成[0...n/2)和[n/2...n)*/
void Divide(int arr[], int n) 
{
	int low = 0, high = n - 1;
	int mid = -1;       //[0...mid) [mid...n)
	while (low < high)
	{
		mid = partition(arr, low, high);
		if (mid == n / 2) //循环终止条件,当mid位于数组中央时
			break;
		else if (mid < n / 2) //在右半部分查找
		{
			low = mid + 1;
		}
		else //在左半部分查找 
		{
			high = mid - 1;
		}
	}
     
}

/*输出数组a[start...end]*/
void OutputArray(int a[], int start, int end)
{
	int i;
	for (i = start; i <= end - 1; i++)
	{
		printf("%d ", a[i]);
	}
	printf("%d\n", a[i]);
}

int main()
{
	int n = 0;
	int a[MaxSize];
	cout << "请输入由正整数构成的集合元素总数: ";
	cin >> n;
	cout << "请依次输入集合元素: ";
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	Divide(a, n);
	cout << "\n划分子集A1:";
	OutputArray(a, 0, n / 2 - 1);
	cout << "        A2:";
	OutputArray(a, n / 2, n - 1);
}

四、运行结果

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值