晴天的魔法乐园——死亡拆分II

原题链接:https://judger.net/problem/1012

Problem Description

给定一个由整数组成的集合,集合中的整数各不相同,现在要将它分为两个子集合,使得这两个子集合的并为原集合、交为空集,同时在两个子集合的元素个数n1与n2之差的绝对值|n1-n2|尽可能小的前提下,要求它们各自的元素之和S1与S2之差的绝对值|S1-S2|尽可能大。

Input

每个输入文件中一组数据。

第一行一个正整数N(1<=N<=10000000),表示集合中的整数个数。第二行是用空格隔开的N个绝对值在6666666以内的整数。

Output

用空格隔开的两个整数,即所求的|n1-n2|与|S1-S2|。

Sample Input

5
4 2 1 3 5

Sample Output

1 9

1、分析

数据量较大,不能使用STL的sort快排,可以使用nth_element()函数,它可以将第几大的元素放到指定的位置上,同时让大于它的元素放到右边,小于的放到左边。题目的大致意思是将集合拆分成两个,使得两个集合中的元素个数之差绝对值要尽量小,即|n1 - n2|尽可能小,同时让两个集合各自元素的和S1和S2的差的绝对值即|S1 - S2|尽可能大。很容易就想到一下方法:

        找出集合的第n/2小元素,计算集合的前n/2个元素和left和全部元素和sum,然后让sum - left得右边集合的元素和,再减left得结果,即:sum- 2 * left,在打印结果的时候需要考虑到n的奇偶性。这种思想可以通过全部测试用例(见代码1),但是却是错误的。考虑下面一个例子:

5
-1 -2 -3 1 2

按照上面的方法,先找出n/2小元素,即-1,然后集合被划分为:-2 -3 -1 1 2,此时前n/2元素和left = -5, sum = -3, sum - 2 * left = 7,于是输出结果为:

1 7

但是仔细想想,似乎有什么问题,当左集合元素和S1 = (-1) + (-2) + (-3) = -6;右集合和S2 = 1 + 2 = 3; |n1 - n2| = 1, 但是此时元素差却更大,|-6 - 3| = 9,即应该输出如下:

1 9

基本思想如下:

       ② 当集合元素为偶数时,直接使用右n/2个元素之和减左n/2个元素和,结果和第一个方法一样,但是当集合个数为奇数的时候,需要根据第n/2个元素正负进行讨论,如果为正,则划归与右边集合,否则为左边集合,才能保证左集合S1-右集合S2绝对值最大,即:

[-2 -3 -1][1 2]比[-2 -3][-1 1 2]更优。

不过这样考虑却不能通过测试用例

2、代码

代码1(可通过测试用例,但是存在问题):

#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
long long n, sum = 0, left = 0, data[10000010];
int main(){
	scanf("%lld", &n);
	for(int i = 0; i < n; i++){
		scanf("%lld", &data[i]);
		sum += data[i];
	}
	//进行划分,将第n/2大元素放在n/2+1位置上,即data[n/2] 
	nth_element(data, data + n / 2, data + n);
	for(int i = 0; i < n / 2; i++){
		left += data[i];
	}
	//注意结果
	printf("%lld %lld\n", n % 2, sum - 2 * left); 
	return 0;
}

代码2(不可通过全部测试用例):

#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
long long n, sum = 0, left = 0, data[10000010];
int main(){
	scanf("%lld", &n);
	for(int i = 0; i < n; i++){
		scanf("%lld", &data[i]);
	}
	//进行划分,将第n/2大元素放在n/2+1位置上,即data[n/2] 
	nth_element(data, data + n / 2, data + n);
	for(int i = 0; i < n / 2; i++){
		sum += data[n - i - 1] - data[i];
	}
	//问题在这里
	if(n % 2 == 0){
		printf("0 %lld\n", sum);
	}else{
		printf("1 %lld\n", sum + abs(data[n / 2]));
	} 
	return 0;
}

参考链接:https://blog.csdn.net/gl486546/article/details/79327824

原文链接:https://www.qsp.net.cn/art/172.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值