题目描述
给定一个由整数组成的集合,集合中的整数各不相同,现在要将它分为两个子集合,使得这两个子集合的并为原集合、交为空集,同时在两个子集合的元素个数n1与n2之差的绝对值|n1-n2|尽可能小的前提下,要求它们各自的元素之和S1与S2之差的绝对值|S1-S2|尽可能大。
输入
每个输入文件中一组数据。
第一行一个正整数N(1<=N<=10000000),表示集合中的整数个数。第二行是用空格隔开的N个在2*10^7以内的正整数。
输出
用空格隔开的两个整数,即所求的|n1-n2|与|S1-S2|。
样例输入
54 2 1 3 5
样例输出
1 9
提示
来源
本题将会使用到上一篇博客中所提到的找第n小的随机选择算法
此题与之前一题较像 传送门
但是数据规模相差较大。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
using namespace std;
int N;
int Randomized_Position(long long A[],int p,int r);
int Randomized_Select(long long A[],int p,int r,int i);
long long A[10000010];
int Randomized_Select(long long A[],int p,int r,int i){
if(p==r)
return A[p];
int q=Randomized_Position(A,p,r);
int k=q-p+1;
if(i==k)
return A[q];
else if(i<k)
return Randomized_Select(A,p,q-1,i);
else
return Randomized_Select(A,q+1,r,i-k);
}
void exchange(int p,int r){
int temp;
temp=A[p];
A[p]=A[r];
A[r]=temp;
}
int Randomized_Position(long long A[],int p,int r){
srand(unsigned(time(0)));
int temp=p+rand()%(r-p+1);
int key=A[temp];
exchange(p,temp);
while(p<r) {
while(p<r&&A[r]>=key) --r;
A[p]=A[r];
while(p<r&&A[p]<=key) ++p;
A[r]=A[p];
}
A[p]=key;
return p;
}
int main(){
scanf("%d",&N);
long long sum0=0,sum1=0;
for(int i=0;i<N;i++){
scanf("%lld",&A[i]);
sum0+=A[i];
}
Randomized_Select(A,0,N-1,N/2);
for(int i=0;i<N/2;i++){
sum1+=A[i];
}
printf("%d %lld\n",N%2,sum0-sum1-sum1);
}
当然还有强大的内置的函数模板nth_element()
函数功能传送门
下列代码为浙大“晴天”提供:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 10000010;
int n; // A存放所有整数,n为其个数
LL A[maxn];
int main() {
// sum和sum1记录所有整数之和与切分后前n/2个元素之和
LL sum = 0, sum1 = 0;
scanf("%d", &n); // 整数个数
for(int i = 0; i < n; i++) {
scanf("%lld", &A[i]); // 输入整数
sum += A[i]; // 累计所有整数之和
}
nth_element(A, A + n / 2, A + n); // 寻找第n/2大的数,并进行切分
for(int i = 0; i < n / 2; i++) {
sum1 += A[i]; // 累计较小的子集合中元素之和
}
printf("%d %lld\n", n % 2, (sum - sum1) - sum1); // 求两个子集合的元素和之差
return 0;
}