C/C++教程

C++解决划分带条件的两子集合

本文主要是介绍C++解决划分带条件的两子集合,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

写好每一份题解,本博文源于胡凡的《算法笔记》,旨在解决分集合,给定一个由整数组成的集合,集合中的整数各不相同,现在要分为两个子集合,使得这两个子集合的并为原集合、交为空集,同时在两个子集合的元素个数n1与n2之差的绝对值 ∣ n 1 − n 2 ∣ |n_1-n_2| ∣n1​−n2​∣尽可能小的前提下,要求它们各自的元素之和 S 1 S_1 S1​与 S 2 S_2 S2​之差的绝对值 ∣ S 1 − S 2 ∣ |S_1-S_2| ∣S1​−S2​∣尽可能大,求这两个|S1-S2|等于多少。

核心思路

最直接的思路是将原集合中的元素从小到大排序,取排序后的前n/2个元素作为其中一个子集合,剩下的元素作为另一个子集合即可。
还有一种就是随机选择,解决的方法是求原集合中的元素的第n/2大,同时根据这个数把集合分为两个部分,使得其中一个子集合都大于这个数,另一个都小于这个数。因此时间复杂度为O(n).

完整代码

#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include <cmath>

using namespace std;
const int maxn = 10010;
int A[maxn],n;
int randPartition(int A[],int left,int right) {
    //生成[left,right]内的随机数p
    int p = (int)(round(1.0*rand()/RAND_MAX*(right-left)+left));
    int temp1 = A[p]; // 将A[left]存放至临时变量temp
    A[p] = A[left];
    A[left] = temp1;

    int temp = A[left];

    while(left<right){
        //只要left与right不相遇
        while(left<right && A[right] > temp) right--; // 反复左移right
        A[left] = A[right];                          //将A[right]挪到
        while(left<right && A[left] <= temp) left++; // 反复右移left
        A[right] = A[left];
    }
    A[left] = temp;
    return left;
}
void randSelect(int A[],int left,int right,int K){
    if(left == right) return ; //边界
    int p = randPartition(A,left,right); //划分后主元的位置P
    int M = p - left + 1; // A[p]是A[left,right]中的第M大
    if(K==M) return ;
    if(K<M){
        //第k大的数在主元左测
         randSelect(A,left,p-1,K);//划分后元素的位置为p
    }else{
        //第K大的数在主元右侧
         randSelect(A,p+1,right,K-M);//往主元右侧找第K-M大的数
    }


}
int main(){
    srand((unsigned)time(NULL));
    //sum和sum1记录所有着呢概述
    int sum = 0,sum1 = 0;
    scanf("%d",&n);
    for(int i = 0;i<n;i++){
        scanf("%d",&A[i]);
        sum += A[i];
    }
    randSelect(A,0,n-1,n/2);
    for(int i = 0;i<n/2;i++)
        sum1 += A[i];
    //求两个子集合的元素和之差
    printf("%d\n",(sum-sum1)-sum1);
    return 0;
}

代码效果

在这里插入图片描述

这篇关于C++解决划分带条件的两子集合的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!