【牛客刷题】带你在牛客刷题第一弹(简单排序)

简介: 这时牛客刷题系列第五弹,本期我们继续来刷一些简单排序题,用来巩固我们的语法知识,也希望大家可以认真学习,加油!

 哈喽,今天是我们牛客刷题训练第一弹,今天我们来刷一些简单排序的问题,这些问题相对于C/C++基础来说难度肯定是高出了一些,但是我相信,只要我们一步步去分析,最后肯定是可以得到正确的答案的,来我们一起加油。

[NOIP2007]纪念品分组

题目描述

元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。

你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

输入描述:

第 1 行包括一个整数 w,为每组纪念品价格之和的上限。第 2 行为一个整数n,表示购来的纪念品的总件数。第 3 ~ n+2 行每行包含一个正整数 p_i ( 5 ≤ p_i ≤ w )pi(5≤pi≤w) ,表示所对应纪念品的价格。

输出描述:

包含一个整数,即最少的分组数目。

示例1

输入

1009902020305060708090

输出

6

备注:

50%的数据满足:1 ≤ n ≤ 151≤n≤15100%的数据满足:1 ≤ n ≤ 30000, 80 ≤ w ≤ 2001≤n≤30000,80≤w≤200

思路

对于这个题目,我们才用的思路是,先将数组里的价格由小到大进行排序,之后我们定义两个指针,分别指向其左右两边,接下来我们从两边到中间同时操作,如果同时指针指向的元素之和比我们的要求要低的话,我们就将这两个元素放入一组,同时两个指针分别向内移动一位;当然如果两个元素之和大于我们的规定金额的话,那么我们就将价格高的元素单独分至一组,之后其指针内移一位,然后继续进行比较。

这样下来就能使分的组数最少。

我们为什么要这样去进行求呢,我们来思考一下,在两端的元素分别是价格最高的元素和价格最低的元素,如果这两个元素之和没有超过我们所规定的金额时,那对于其他元素来说,都可以与第一个元素所组合,但是最大的元素并不能确定可以跟第一个元素后面的元素进行组合,那我们将这两个元素放在一起,就可以很好的避免一个单独的分组出现,同理倒数第二个,倒数第三个也可以这样思考。

当我们两个价格相加均大于其总金额时,这时我们应该将较大的元素拿出,因为在此时,我们左边元素是没有分组的最小元素,所以当这时都会大于其最大金额时,我们其他的元素与右边元素相加肯定也会大于其规定金额的,那我们就将最大的元素单独分组,然后再进行操作就可以了。

AC

#include<iostream>#include<algorithm>usingnamespacestd;
constintN=3e4+10;
inta[N];  // 用来存储纪念品的价格 intmain() {
intw;  // 每组纪念品价格之和的上限。intn;  // 纪念品的个数 cin>>w>>n;
for (inti=1; i<=n; i++) {  // 输入 n 个纪念品的价格 cin>>a[i];
    }
sort(a+1, a+n+1);  // 将纪念品的价格升序排序(按照价格从低到高进行排列) intl=1;  // 左边的指针(其实是表示下标的) intr=n;  // 右边的指针(其实是表示下标的)intcnt=0;  // 计数器,用来记录组数 while (l<=r) {  // 当左边的指针小于等于右边的指针时,记得要有等号 if (a[l] +a[r] <=w) {  // 若两者之和小于等于 w,可以分成一组 l++;  // 左边的指针(下标)加 1 r--;  // 右边的指针(下标)减 1cnt++;  // 计数器加 1,a[l] 和 a[r] 作为一组,组数加 1         } 
else {  // 若不能分成一组 r--;   // 右边的指针(下标)减 1 (左边的指针不变)cnt++;   // 计数器加 1,a[r] 自己作为一组         }
    }
cout<<cnt;  // 输出计数器,即为最少的组数 return0;
}

image.gif

运行结果:

1.png

[NOIP2007]统计数字

题目描述

某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*109)。已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。

输入描述:

第1行是整数n,表示自然数的个数。

第2~n+1行每行一个自然数。

输出描述:

输出m行(m为n个自然数中不相同数的个数),按照自然数从小到大的顺序输出。每行输出两个整数,分别是自然数和该数出现的次数,其间用一个空格隔开。

示例1

输入

8242451002100

输出

2 34 25 1100 2

备注

40%的数据满足:1 ≤ n ≤ 1000

80%的数据满足:1 ≤ n ≤ 50000

100%的数据满足:1 ≤ n ≤ 200000,每个数均不超过1500000000(1.5*109)

思路

这道题目我在这里使用的是一种非常简单的方法,就是首先我们先将数组里的元素按从小到大进行排序,排序完成后我们设定一个参照数和一个总数(每个元素的)

然后我们从前到后逐个遍历,如果后面元素与前面相同就总数++,否则的话我们输出前面一个元素和总数,之后再将总数设为1(切记这里要设置成1)因为我们当前元素与前面元素不想等,这就是一个新的元素,也就是我们这个元素也就是第一个。

就这样我们循环完成后,对应的输出也就完成了。

AC

#include<iostream>#include<algorithm>usingnamespacestd ;
inta[200010] ;    //数组存放所有数intmain()
{
intn ;
cin>>n ;
for(inti=0; i<n; i++)
    {
cin>>a[i] ;
    }
sort(a, a+n) ;       //从小到大进行排序intsum=0 ;    //相同数字元素个数ints=a[0] ;    //进行比较的元素for(inti=0; i<n; i++)
    {
if(s==a[i])    sum++ ;    //如果后面元素与前面相同else{
cout<<a[i-1] <<" "<<sum<<endl ;    //后面元素与前面不同s=a[i] ;
sum=1 ;
            }
        }
cout<<a[n-1] <<" "<<sum<<endl ;    //输出最后一组数据return0 ;
}

image.gif

运行结果:

2.png

[NOIP2004]火星人

题目描述

人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。

火星人用一种非常简单的方式来表示数字——掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3……。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。

一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指——拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个3位数和它们代表的数字:

三进制数

123
 132
 213
 231
 312
 321

image.gif

代表的数字

1
 2
 3
 4
 5
 6

image.gif

现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。

输入描述:

第一行有一个正整数N,表示火星人手指的数目(1 <= N <= 10000)(1≤N≤10000)。第二行是一个正整数M,表示要加上去的小整数(1 <= M<= 100)(1≤M≤100)。下一行是1到N这N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。

输出描述:

输出只有一行,这一行含有N个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。

示例1

输入

531 2 3 4 5

输出

1 2 4 5 3

思路

这道题目有点难理解,我来给大家解释一下。

1.获取输入n,m

2.获取火星人手指的排列顺序,存放在数组a[N]中

3.加m的结果相当于,以当前排列顺序为基础,进行m次全排列后的排列顺序。因此循环m次,调用next_permutation函数进行全排列

4.输出m次全排列后的数组a,以空格隔开

是不是解释完之后就发现题目变得清晰了很多。

全排列:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。

所以我们可以调用C++STL库中的函数,也就是next_permutation ,也就是全排列函数;

其函数原型为:

#include <algorithm>
     bool next_permutation(iterator start,iterator end)

image.gif

next_permutation(num,num+n)函数是对数组num中的前n个元素进行全排列,同时并改变num数组的值。

大概就是这么个意思, 所以我们只需执行m次全排列就可以了。

AC

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std ;
const int max_n = 10010;
int num[max_n] ;
int main()
{
    int n,m ;
    while( scanf("%d%d",&n,&m) == 2)
    {
        for( int i = 0; i < n; i++)
            scanf("%d",&num[i]);
        for( int i = 0; i < m; i++)
            next_permutation(num,num+n);    //进行m此全排列
        for( int i = 0; i < n; i++)
            printf(i==n-1?"%d\n":"%d ",num[i]);
    }
    return 0;
}

image.gif

运行结果:

3.png

目录
相关文章
|
5月前
|
算法
OJ刷题:杨氏矩阵
OJ刷题:杨氏矩阵
25 0
|
6月前
【一刷《剑指Offer》】面试题 6:重建二叉树
【一刷《剑指Offer》】面试题 6:重建二叉树
|
6月前
【一刷《剑指Offer》】面试题 16:反转链表
【一刷《剑指Offer》】面试题 16:反转链表
|
6月前
|
算法 索引
OJ刷题日记:5、二分查找(1)
OJ刷题日记:5、二分查找(1)
53 0
|
6月前
剑指Offer(第二版)04
剑指Offer(第二版)04
20 0
|
6月前
剑指Offer(第二版)11
剑指Offer(第二版)11
33 0
|
6月前
剑指Offer(第二版)10-2
剑指Offer(第二版)10-2
31 0
|
6月前
剑指Offer(第二版)06
剑指Offer(第二版)06
29 0
|
6月前
剑指Offer(第二版)03
剑指Offer(第二版)03
27 0
|
算法
AcWing刷题(第二周)(链表,单调栈等......)
AcWing刷题(第二周)(链表,单调栈等......)
78 0