FZU ICPC 2020 寒假训练 5 —— 排序

P1177 【模板】快速排序

题目描述

利用快速排序算法将读入的 N 个数从小到大排序后输出。快速排序是信息学竞赛的必备算法之一。对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成。(C++ 选手请不要试图使用 STL,虽然你可以使用 sort 一遍过,但是你并没有掌握快速排序算法的精髓。)

输入格式

第 1 行为一个正整数 N,第 2 行包含 N 个空格隔开的正整数 ai,为你需要进行排序的数,数据保证了 Ai不超过 1e9。

输出格式

将给定的 N 个数从小到大输出,数之间空格隔开,行末换行且无空格。

输入 #1

5
4 2 4 5 1

输出 #1

1 2 4 4 5

Accepted

本题如果用sort函数是直接过的,但是为了更好的理解快排,我参考了题解的方法。同时看到了很多种的排序方式(今天做完题目就开始学习!)来源:洛谷
快速排序(quicksort)是20世纪60年代提出的一种算法,类似于归并,也有分治的一种思想,即找到一个基准数,将比它大的放在它的右边,比它小的放在它的左边。
1.基准数位置。
基准数的位置在教科书中一般选为最左边或最右边,我一般习惯于选最左边。不同选择对执行没有较大的影响。
2.左右移动的方式
目前我所见到的移动的方式,主要有两种,
一是j(从右)找到一个比基准数(记作k)小的数,然后让i(从左)找到一个比k大的数,然后进行交换。直到i,j相遇开始分治处理;
另一种是 基准数记作k,基准数下标记作x
① i(从左)找到一个比k大的数,a[x]=a[i];
② j(从右)找到一个比k小的数,a[i]=a[j];
③ i(从左)找到一个比k大的数,a[j]=a[i];
④ 重复②③直到i==j;
⑤ 将a[i]赋值为基准数k;
第一种是快排的原始思想,交换->分治,但是借用了t来交换a[i]和a[j];
第二种的思想巧妙一些,没有用一次三句话操作,而是每次交换,都可以保证a[i]>a[j]。

#include <stdio.h>
#include <math.h>
#include <string.h>
    int a[1000000];
void sort(int left,int right){
    int mid=a[(left+right)/2];//中间数
    int i=left,j=right;
    while(i<=j){
        while(a[i]<mid) i++;//查找左半部分比中间数大的数
        while (a[j]>mid) j--;//查找右半部分比中间数小的数
        if(i<=j){//如果有一组不满足排序条件(左小右大)的数
            int temp;
            temp=a[i];
            a[i]=a[j];
            a[j]=temp;
            i++;
            j--;
        }
    }
    if(left<j) sort(left,j);
    if(right>i) sort(i, right);
}
       int main(){
           int n;
           scanf("%d",&n);
           for(int i=0;i<n;i++){
               scanf("%d",&a[i]);
           }
           sort(0,n-1);
           for(int i=0;i<n;i++){
               printf("%d",a[i]);
               if(i!=n-1) printf(" ");
           }
           return 0;
       }

P1068 分数线划定

题目描述

世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的150%划定,即如果计划录取m名志愿者,则面试分数线为排名第m×150%(向下取整)名的选手的分数,而最终进入面试的选手为笔试成绩不低于面试分数线的所有选手。
现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。

输入格式

第一行,两个整数 n,m(5≤n≤5000,3≤m≤n),中间用一个空格隔开,其中n表示报名参加笔试的选手总数,m表示计划录取的志愿者人数。输入数据保证 m×150%向下取整后小于等于 n。
第二行到第 n+1 行,每行包括两个整数,中间用一个空格隔开,分别是选手的报名号 k(1000≤k≤9999)和该选手的笔试成绩s(1≤s≤100)。数据保证选手的报名号各不相同。

输出格式

第一行,有2个整数,用一个空格隔开,第一个整数表示面试分数线;第二个整数为进入面试的选手的实际人数。
从第二行开始,每行包含2个整数,中间用一个空格隔开,分别表示进入面试的选手的报名号和笔试成绩,按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。

输入 #1

6 3
1000 90
3239 88
2390 95
7231 84
1005 95
1001 88

输出 #1

88 5
1005 95
2390 95
1000 90
1001 88
3239 88

说明/提示

【样例说明】
m×150%=3×150%=4.5,向下取整后为4。保证4个人进入面试的分数线为88,但因为88有重分,所以所有成绩大于等于
88 的选手都可以进入面试,故最终有5个人进入面试。

Accepted

这题一开始我还是使用快排,但是后面发现我无法就当分数相等时,对编号进行排序,就采用了sort函数+结构体的方法.

#include <stdio.h>
#include <math.h>
#include <string.h>
#include<algorithm>
struct player{
    int s;
    int k;
}a[100000];
bool cmp(player a1,player b1){ //注意这一步
    if(a1.s==b1.s) return a1.k<b1.k;
    return a1.s>b1.s;
}
 int main(){
        int n,i;
        int m;
        int num=0;
        int score_num=0;
        int score=0;
        scanf("%d %d",&n,&m);
        score_num=(int)m*1.5;
        for(i=1;i<=n;i++)
            scanf("%d %d",&a[i].k,&a[i].s);
     std::sort(a+1,a+n+1,cmp);
     score=a[score_num].s;
     for(i=1;i<=n;i++){
         if(a[i].s<score) a[i].s=0;
     }
     for(i=1;i<=n;i++){
         if(a[i].s) num++;
     }
     printf("%d %d\n",score,num);
     for(i=1;i<=n;i++){
         if(a[i].s) printf("%d %d\n",a[i].k,a[i].s);
     }
            return 0;
        }

P1781 宇宙总统

题目描述

地球历公元 6036 年,全宇宙准备竞选一个最贤能的人当总统,共有 n 个非凡拔尖的人竞选总统,现在票数已经统计完毕,请你算出谁能够当上总统。

输入格式

第一行为一个整数 n,代表竞选总统的人数。接下来有 n 行,分别为第一个候选人到第 n 个候选人的票数。

输出格式

共两行,第一行是一个整数 m,为当上总统的人的号数。第二行是当上总统的人的选票。

输入 #1

5
98765
12365
87954
1022356
985678

输出 #1

4
1022356

说明/提示

票数可能会很大,可能会到100 位数字。

Accepted

这题看到提示就明白要对字符串进行比较,也是这一题让我看到了c++在对字符串进行操作时比c更加便利的地方:

#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
int main(){
       int n,i;
       int num = 0;
       string str_max="";
       string str;
      cin >> n;
      for(i=1;i<=n;i++){
          cin >> str;
          int str_size=(int)str.size();
          int str_max_size=(int)str_max.size();
          if(str_size>str_max_size||(str_size>=str_max_size&&str>str_max)){
              str_max=str;
              num=i;
          }
    }
    cout << num << endl << str_max << endl;
           return 0;
       }

当然这一题也可以用结构体+sort函数解决会比用c然后一位位比快的多(来源:洛谷):

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    string x; //装票数
    int num;  //装号数
    int lenx; //装票数的位数
}s[25];
bool cmp(node a,node b)
{
    if(a.lenx>b.lenx) return 1; //前一个比后一个位数多,不交换
    if(a.lenx==b.lenx&&a.x>b.x) return 1; //位数相同,但前一个按字典序排列比后一个大,也不交换。
    return 0; //剩下情况均要交换。
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i].x;
        s[i].num=i; //存号数
        s[i].lenx=s[i].x.size(); //存票数的位数
    }
    sort(s+1,s+n+1,cmp); //排序
    cout<<s[1].num<<endl;  //输出首位答案即可,注意先输出号数
    cout<<s[1].x<<endl; //再输出票数
    return 0;
}

P1309 瑞士轮

题目背景

在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。

题目描述

2×N 名编号为 1∼2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第 1 名和第 2 名、第 3 名和第 4名、……、第2K−1名和第2K名、…… 、第2N−1名和第2N名,各进行一场比赛。每场比赛胜者得1分,负者得 0分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。

输入格式

第一行是三个正整数N,R,Q,每两个数之间用一个空格隔开,表示有 2×N名选手、R 轮比赛,以及我们关心的名次 Q。
第二行是2×N 个非负整数s1,s2,…,s2N,每两个数之间用一个空格隔开,其中si表示编号为i 的选手的初始分数。 第三行是2×N 个正整数w1,w2,…,w2N,每两个数之间用一个空格隔开,其中 wi表示编号为i 的选手的实力值。

输出格式

一个整数,即R 轮比赛结束后,排名第Q 的选手的编号。

输入 #1

2 4 2
7 6 6 7
10 5 20 15

输出 #1

1

Wrong Answer

#include<algorithm>
#include <stdio.h>
struct player{
    int s;
    int w;
    int num;
}a[1000000];
bool cmp(player a, player b){
    if(a.s==b.s) return a.num<b.num;
    return a.s>b.s;
}
int main(){
    int n,i,q,r;
    scanf("%d %d %d",&n,&r,&q);
    for(i=1;i<=2*n;i++){
        scanf("%d",&a[i].s);
        a[i].num=i;
    }
    for(i=1;i<=2*n;i++){
        scanf("%d",&a[i].w);
    }
    while(r--){
        std::sort(a+1,a+1+2*n,cmp);
        for(i=1;i<2*n;i+=2){
            if(a[i].w>a[i+1].w) a[i].s++;
                else a[i+1].s++;
        }
    }
        std::sort(a+1,a+1+2*n,cmp);
    printf("%d\n",a[q].num);
    return 0;
}

Accepted

这题我刚刚开始采用快排,但是仍有两个点没过去,开了O2才AC,后面看了题解才知道,这一题需要用到的是归并排序的方法:(来源:洛谷)
一、关于sort的浪费
首先让我们想想,sort其实就是快速排序。稳定的话O(nlogn)左右。但是仔细想想此题——每次需要更新的值,都是相邻两个人变化后的分数;而相邻的分数,有些是不会改变位置的,而快速排序则是每次全部修改,必然会造成浪费。
二、关于归并排序
然后考虑归并排序: 归并排序的思想就是合并两个同序数组的线性方式——每次比较两个有序数组指针指向的值,谁更小(大)则放到temp数组里,然后删掉进入temp的元素,指针++。

#include<algorithm>
#include <stdio.h>
struct player{
    int s;
    int num;
}a[2000005];
player v[200005],l[200005];//v:胜者组  l:败者组
bool cmp(player a, player b){
    if(a.s==b.s) return a.num<b.num;
    return a.s>b.s;
}
//将胜者组v和败者组l合并到stu中
void merge(int n){
    int i=1,j=1,k=1;
    while(i<=n&&j<=n){
        if(v[i].s>l[j].s||(v[i].s==l[j].s&&v[i].num<l[j].num)){
            a[k].s=v[i].s;
            a[k].num=v[i].num;
            k++;
            i++;
        }
        else{
            a[k].s=l[j].s;
            a[k].num=l[j].num;
            k++;
            j++;
        }
    }
    while(i<=n){
        a[k].s=v[i].s;
        a[k].num=v[i].num;
        k++;
        i++;
    }
    while(j<=n){
        a[k].s=l[j].s;
        a[k].num=l[j].num;
        k++;
        j++;
    }
}
int main(){
    int n,i,q,r;
    int w[2000005];
    scanf("%d %d %d",&n,&r,&q);
    for(i=1;i<=2*n;i++){
        scanf("%d",&a[i].s);
        a[i].num=i;
    }
    for(i=1;i<=2*n;i++){
        scanf("%d",&w[i]);
    }
    std::sort(a+1,a+1+2*n,cmp);//【快排】(未开始比赛前,所有选手都是无序的)
    for(int t=1;t<=r;t++){
        int ind=1;//// 胜者组a和败者组b的下标
        for(i=1;i<2*n;i+=2){
            if(w[a[i].num]>w[a[i+1].num]){
                v[ind].s=a[i].s+1;
                v[ind].num=a[i].num;
                l[ind].s=a[i+1].s;
                l[ind].num=a[i+1].num;
                ind++;
            }
            else{
                    v[ind].s=a[i+1].s+1;
                    v[ind].num=a[i+1].num;
                    l[ind].s=a[i].s;
                    l[ind].num=a[i].num;
                    ind++;
                
            }
        }
        merge(n);
    }
        
    printf("%d\n",a[q].num);
    return 0;
}
posted @ 2020-02-10 18:56  平和Kenn  阅读(290)  评论(0编辑  收藏  举报