天天看点

新手入门刷题(专题二)排序 (第二部分)

新手入门刷题(专题二)排序

4.12

宇宙总统

题目描述

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

输入样例

5	//参赛人数
98765	//得票数 票数可能会很大,可能会到 100位数字。
12365
87954
1022356
985678
           

输出样例

4
1022356
           

思路

本题的票数用string存储,比较时先比较票长,长度一致则比较大小。

代码

#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct Candidate {
    int num;//序号
    string grade;//票数
} pre[25];
//自定义比较函数
bool cmp(Candidate a,Candidate b){
    if(a.grade.length()>b.grade.length()) return a.grade.length()>b.grade.length();
    else if(a.grade.length()==b.grade.length()) return a.grade>b.grade;
    return false;
}
int main() {
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>pre[i].grade;
        pre[i].num = i;
    }
    sort(pre+1,pre+n+1,cmp);
    cout<<pre[1].num<<endl<<pre[1].grade;
    return 0;
}
           

[USACO07DEC]Bookshelf B

题目描述

略,求最少几个奶牛即可到书架高度

输入样例

6 40	//奶牛数为6,书架高为40
6	//第一个奶牛高度
18
11
13
19
11
           

输出样例

3
           

思路

先快排所有奶牛高度,再从大到小相加,直到>=书架高度,统计相加的奶牛个数

代码

#include <iostream>
#include<algorithm>
using namespace std;
int n,b,h[20020],t,k;
//快排模板要记住
int qs(int q[],int l,int r){
    if(l>=r) return q[l];
    int i = l-1,j = r+1, x = q[l+r>>1];
    while(i<j){
        do i++;while(q[i]<x);
        do j--;while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    qs(q, l, j);
    qs(q, j + 1, r);
}
int main() {
    cin>>n>>b;
    for(int i=0;i<n;i++) cin>>h[i];
    qs(h,0,n-1);
    while(t<b&&k<n){
        t += h[n-1-k];
        k++;
    }
    cout<<k;
    return 0;
}
           

车厢重组

题目描述

好家伙,考查冒泡可还行。

代码

#include <iostream>
#include<algorithm>
using namespace std;
int n,t[10010],times;
int main() {
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&t[i]);
    }
    for(int i=0;i<n-1;i++){
        for(int j=i+1;j<n;j++){
            if(t[i]>t[j]) {
                swap(t[i],t[j]);
                times++;
            }
        }
    }
    printf("%d",times);
    return 0;
}
           

欢乐的跳

题目描述

一个包含n个元素的整数数组,如果数组两个连续元素之间差的绝对值包括了[1,n-1]之间的所有整数,则称之符合“欢乐的跳”,如数组 {1,4,2,3} 符合“欢乐的跳”,因为差的绝对值分别为:3,2,1。

给定一个数组,你的任务是判断该数组是否符合“欢乐的跳”。

输入样例

5 1 4 2 -1 6	//第一个数字代表数组长度,之后代表每一个数字
           

输出样例

jolly	//符合“欢乐的跳”输出
Not jolly	//不符合“欢乐的跳”输出
           

思路

数组长度 n ( 1 ≤ n ≤ 1000 )

数组中的数字范围 [-100000000,100000000]

本题提供两种解法

第一种用桶装两数之差,即采用数组下标的方式存储两数之差,不过数组的长度要大于100000000。

只需要数组范围 [1,n-1] 之内的数全是 1 即符合“欢乐的跳”,具体见代码一。

第二种方法,我们可以研究一下题目,显然在存数组的过程中即可计算两数之差,一旦两数之差出现重复,一定不符合“欢乐的跳”,具体见代码二。

代码一

#include <iostream>
#include<algorithm>
using namespace std;
int n,t[1010],num[200000000];
int main() {
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&t[i]);
    }
    //桶
    for(int i=0;i<n-1;i++){
        int tmp;
        tmp = t[i]>=t[i+1]?t[i]-t[i+1]:t[i+1]-t[i];
        num[tmp]++;
    }
    for(int i=1;i<n;i++){
        if(num[i]==0){
            printf("Not jolly");
            return 0;
        }
    }
    printf("Jolly");
    return 0;
}
           

代码二

#include <iostream>
using namespace std;
int n,a,b,c;
bool d[1010];
int main() {
    //输入数组长度
    scanf("%d",&n);
    //输入第一个数
    scanf("%d",&a);
    //从第二个数开始输入
    for(int i=1;i<n;i++){
        scanf("%d",&b);
        //计算两数之差
        c = a>=b?a-b:b-a;
        //两数之差不在[1,n-1]之内,必定不符合
        if(c>=1&&c<=n-1){
            //判断是否重复,重复也必定不符合
            if(d[c]) {
                printf("Not jolly");
                return 0;
            }
            //不重复,则继续
            else d[c] = true;
        }else {
            printf("Not jolly");
            return 0;
        }
        //赋值第二个数给第一个数,开启下轮循环
        a = b;
    }
    //能够完成输入循环,则必定符合
    printf("Jolly");
    return 0;
}
           

4.13

[NOIP2009 普及组] 分数线划定

题目描述

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

现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。

输入样例

6 3 //报名参加笔试的选手总数 计划录取的志愿者人数
1000 90 //选手的报名号 选手的笔试成绩
3239 88 
2390 95 
7231 84 
1005 95 
1001 88
           

输出样例

88 5 
1005 95 
2390 95 
1000 90 
1001 88 
3239 88 
//按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。
           

说明/提示

【样例说明】

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

思路

学习新排序啦, 归并排序, 时间复杂度nlogn, 稳定!!!

快排时间复杂度为nlogn, 但是不稳定, 本题如果成绩相同时按输入先后顺序输出, 则使用快排不合适, 因为不稳定会导致成绩相同但是输出顺序与输入顺序不一致.

但是本题要求根据 成绩从大到小 和 成绩相同则根据编号从小到大输出, 因此使用 快排 和 归并排序 皆可

归并排序模板

const int N=1e6+10;
int q[N],temp[N];
void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;
    int mid = (l + r) >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        //正序
        if (q[i] <= q[j]) tmp[k++] = q[i++];
    	//逆序	
   	    //if (q[i] >= q[j]) tmp[k++] = q[i++];
        else tmp[k++] = q[j++];
    while (i <= mid) tmp[k++] = q[i++];
    while (j <= r) tmp[k++] = q[j++];
    for (int i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
           

采用归并排序解题代码

#include<iostream>
using namespace std;
struct Emp{
    int num;
    int grade;
}emp[5050],tmp[5050];
int n,m;
//根据归并排序模板修改,稳定
void merge_sort(int l, int r)
{
    if (l >= r) return;
    int mid = (l + r) >> 1;
    merge_sort(l, mid);
    merge_sort(mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        //成绩从大到小
        if (emp[i].grade > emp[j].grade) tmp[k++] = emp[i++];
    	//成绩相同,编号从小到大
        else if (emp[i].grade == emp[j].grade && emp[i].num <= emp[j].num) tmp[k++] = emp[i++];
        else tmp[k++] = emp[j++];
    while (i <= mid) tmp[k++] = emp[i++];
    while (j <= r) tmp[k++] = emp[j++];
    for (int i = l, j = 0; i <= r; i++, j++) emp[i] = tmp[j];
}
int main(){
    scanf("%d%d",&n,&m);
    m = m * 1.5;
    for(int i=1;i<=n;i++) scanf("%d%d",&emp[i].num,&emp[i].grade);
    merge_sort(1,n);
    //成绩相同时接着往下找
    while(emp[m].grade==emp[++m].grade);
    printf("%d %d\n",emp[m].grade,--m);
    for(int i=1;i<=m;i++) printf("%d %d\n",emp[i].num,emp[i].grade);
    return 0;
}
           

采用快排解题代码

#include<iostream>
using namespace std;
struct Emp{
    int num;
    int grade;
}emp[5050];
int n,m;
//改写快排
void qs(int l,int r){
    if(l>=r) return;
    int i = l-1, j = r+1, x = emp[(r+l)>>1].grade, y = emp[(r+l)>>1].num;
    while(i<j){
        //成绩从大到小 成绩相同,编号从小到大
        do i++;while(emp[i].grade>x||(emp[i].grade==x&&emp[i].num<y));
        do j--;while(emp[j].grade<x||(emp[j].grade==x&&emp[j].num>y));
        if(i<j) swap(emp[i],emp[j]);
    }
    qs(l,j);
    qs(j+1,r);
}
int main(){
    scanf("%d%d",&n,&m);
    m = m * 1.5;
    for(int i=1;i<=n;i++) scanf("%d%d",&emp[i].num,&emp[i].grade);
    qs(1,n);
    //成绩相同时接着往下找
    while(emp[m].grade==emp[++m].grade);
    printf("%d %d\n",emp[m].grade,--m);
    for(int i=1;i<=m;i++) printf("%d %d\n",emp[i].num,emp[i].grade);
    return 0;
}
           

攀爬者

题目描述

HKE考完GDOI之后跟他的神犇小伙伴们一起去爬山。

他在地形图上标记了N个点,每个点Pi都有一个坐标(xi,yi,zi)。所有点对中,高度值z不会相等。HKE准备从最低的点爬到最高的点,他的攀爬满足以下条件:

(1) 经过他标记的每一个点;

(2) 从第二个点开始,他经过的每一个点高度z都比上一个点高;

(3) HKE会飞,他从一个点Pi爬到Pj的距离为两个点的欧几里得距离。即,

( X i − X j ) 2 + ( Y i − Y j ) 2 + ( Z i − Z j ) 2 \sqrt{(X_i-X_j)^2+(Y_i-Y_j)^2+(Z_i-Z_j)^2} (Xi​−Xj​)2+(Yi​−Yj​)2+(Zi​−Zj​)2

现在,HKE希望你能求出他攀爬的总距离。

输入样例

5	//点的个数
2 2 2	//x y z
1 1 1
4 4 4
3 3 3
5 5 5
           

输出样例

6.928
           

说明/提示

【样例说明】

对于100%的数据,1≤N≤50000,答案的范围在double范围内。

思路

先按照z轴排序, 再计算两点距离.

代码

#include<iostream>
#include<math.h>
#include<cmath>
#include<algorithm>
using namespace std;
double ans;
struct Node { 
    int x,y,z; 
    //重载<
    bool operator < (const Node &b)const{
		return z<b.z;
	}
} a[50050];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    sort(a,a+n);
    for(int i=1;i<n;i++) ans+=sqrt(pow(a[i].x-a[i-1].x,2)+pow(a[i].y-a[i-1].y,2)+pow(a[i].z-a[i-1].z,2));
    printf("%.3lf",ans);
    return 0;
}
           

4.14

生日

题目描述

cjf

君想调查学校 OI 组每个同学的生日,并按照年龄从大到小的顺序排序,如果有两个同学生日相同,输入靠后的同学先输出。但

cjf

君最近作业很多,没有时间,所以请你帮她排序。

输入样例

3	//人数
Yangchu 1992 4 23	//姓名 生日
Qiujingya 1993 10 13
Luowen 1991 8 1
           

输出样例

Luowen	//姓名
Yangchu
Qiujingya
           

说明/提示

数据规模

1<n<100

length(s)<20

思路

结构体加sort函数,本题和上一题基本一样。

代码

#include<iostream>
#include<algorithm>
using namespace std;
struct Students { 
    int nu;
    char na[25];
    int ye;
    int mo;
    int da;
    //sort默认从小到大排序,我们需要重载 < 运算符号
    //重载<
    bool operator < (const Students &b)const{
        if(ye<b.ye) return ye<b.ye;
        else if(ye==b.ye&&mo<b.mo) return mo<b.mo;
        else if(ye==b.ye&&mo==b.mo&&da<b.da) return da<b.da;
        else if(ye==b.ye&&mo==b.mo&&da==b.da&&nu>b.nu) return nu>b.nu;
        return false;
	}
} a[110];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        a[i].nu = i;
        //提供两种输入方法
        //小知识,字符串名称本身就是地址,无需添加地址符
        // scanf("%s %d %d %d",a[i].na,&a[i].ye,&a[i].mo,&a[i].da);
        cin>>a[i].na>>a[i].ye>>a[i].mo>>a[i].da;
    } 
    sort(a,a+n);
    for(int i=0;i<n;i++) printf("%s\n",a[i].na);
    return 0;
}
           

[NOIP1998 提高组] 拼数

题目描述

设有 n个正整数 a1…an,将它们联接成一排,相邻数字首尾相接,组成一个最大的整数。

输入样例

4
7 13 4 246
           

输出样例

7424613
           

说明/提示

对于全部的测试点,保证

1 ≤ n ≤ 20 1≤n≤20 1≤n≤20

1 ≤ a i ≤ 1 0 9 1≤ai≤10^9 1≤ai≤109

思路

只需要判段两数先后相加的大小即可。

代码

#include<iostream>
#include<algorithm>
using namespace std;
string s[25];
//
bool cmp(string a,string b){
    return a+b>b+a;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++) cin>>s[i];
    sort(s,s+n,cmp);
    for(int i=0;i<n;i++) cout<<s[i];
    return 0;
}