27735: 死亡拆分II

题目描述

给定一个由整数组成的集合,集合中的整数各不相同,现在要将它分为两个子集合,使得这两个子集合的并为原集合、交为空集,同时在两个子集合的元素个数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

提示

来源

Shoutmon




本题将会使用到上一篇博客中所提到的找第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;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值