PAT甲级1113.

目录

A1113

题目

样例

样例1

输入:

10
23 8 10 99 46 2333 46 1 666 555

输出:

0 3611

样例2

输入:

13
110 79 218 69 3721 100 29 135 2 6 13 5188 85

输出:

1 9359

思路和坑点

  思路:
  此题其实就是对数组进行对半划分,小的部分在一侧,大的部分在另一侧,如果是偶数个元素,则对半划分,如果是奇数个元素,将位于中间的那个元素归在数值较大的部分。最后,对两部分进行累加求和并输出结果即可。可以直接对整个数组进行排序然后统计,也可以采用快排划分的思路进行划分(不需要全部排序),只要中间位置的元素划分划分,就可以满足题目要求的划分条件。

AC代码(暴力解法)

#include<bits/stdc++.h>
using namespace std;
int main(){    
#ifdef ONLINE_JUDGE    
#else    
    freopen("1.txt", "r", stdin);    
#endif    
    int n;
    scanf("%d",&n);         
    vector<int> v(n);
    for(int i=0;i<n;i++)            //读入数据
        scanf("%d",&v[i]);
    //根据题意确定n1和n2,
    int n1=n/2;                     //由题意可知,n1、n2需要尽可能的个数上接近;S1和S2的插值需要尽可能的大,所以也就是排序后的前一半和后一半
    int n2=n-n1;                    //由于元素个数奇数个和偶数个的情况不同,偶数个则对半分,奇数个(从小到大排序),后半部分比前部分多一个元素
    sort(v.begin(),v.end());        //从小到大排序
    int s1=0,s2=0;                  //统计两部分的和
    for(int i=0;i<n1;i++) s1+=v[i];
    for(int i=n1;i<n;i++) s2+=v[i];
    printf("%d %d",n2-n1,s2-s1);    //按照要求进行输出
    return 0;    
}

AC代码(划分解法)

  其他同学提供的划分算法,顺利AC,划分方法的区别在此不做说明。

#include<bits/stdc++.h>
using namespace std;
int main(){    
#ifdef ONLINE_JUDGE    
#else    
    freopen("1.txt", "r", stdin);    
#endif   
    int n,n1,n2;
    scanf("%d",&n);         
    int *v=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++)            //读入数据
        scanf("%d",&v[i]);
    int low_tmp=0,high_tmp=n,tmp;
    while(1){                       //快排划分方法进行划分
        int low=low_tmp,high=high_tmp;
        tmp=v[low];
        while(1){
            while(v[--high]>tmp) if(high==low_tmp) break;
            while(v[++low]<tmp) if(low==high_tmp) break;
            if(low>=high) break;
            swap(v[low],v[high]);
        }
        swap(v[low_tmp],v[high]);
        if(high==n/2) break;         //如果完成划分则停止
        else if(high<n/2) low_tmp=++high;
        else               high_tmp=high;
    }
    n1=n/2;
    n2=n-n1;                        //由于元素个数奇数个和偶数个的情况不同,偶数个则对半分,奇数个(从小到大排序),后半部分比前部分多一个元素
    int s1=0,s2=0;                  //统计两部分的和
    for(int i=0;i<n1;i++) s1+=v[i];
    for(int i=n1;i<n;i++) s2+=v[i];
    printf("%d %d",n2-n1,s2-s1);    //按照要求进行输出
    return 0;    
}

未AC代码(划分解法)

  此方法测试点3超时,算法逻辑没有错误,但是应对超长有序(和划分的排序规则相同的有序)序列时耗时过长导致程序超时。

#include<bits/stdc++.h>
using namespace std;
int main(){    
#ifdef ONLINE_JUDGE    
#else    
    freopen("1.txt", "r", stdin);    
#endif    
    int n,n1,n2;
    scanf("%d",&n);         
    int *v=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++)            //读入数据
        scanf("%d",&v[i]);
    int low=0,high=n-1,tmp;
    int low_tmp=low,high_tmp=high;  //更新记录上一次划分的边界
    while(1){                       //快排划分方法进行划分
        tmp=v[low];
        while(low<high){
            while(low<high&&v[high]>=tmp) high--;
            v[low]=v[high];
            while(low<high&&v[low]<=tmp) low++;
            v[high]=v[low];
        }
        v[low]=tmp;
        if(low==n/2) break;         //如果完成划分则停止,如果未完成则继续对包含了中间值的哪个部分进行划分
        else if(low<n/2) { low_tmp=++low,high=high_tmp; }
        else             { low=low_tmp,high_tmp=--high; }
    }
    n1=n/2;
    n2=n-n1;                        //由于元素个数奇数个和偶数个的情况不同,偶数个则对半分,奇数个(从小到大排序),后半部分比前部分多一个元素
    int s1=0,s2=0;                  //统计两部分的和
    for(int i=0;i<n1;i++) s1+=v[i];
    for(int i=n1;i<n;i++) s2+=v[i];
    printf("%d %d",n2-n1,s2-s1);    //按照要求进行输出
    return 0;    
}
/*
处理数据超时的情况:超大有序序列,在划分是时间花费比较多,比如
100000
全是1
*/