备战蓝桥杯-枚举、排序、模拟专项练习详解(含有多道蓝桥杯原题)

枚举、模拟与排序


蓝桥杯所有专项练习

蓝桥杯原题: 连号区间数

小明这些天一直在思考这样一个奇怪而有趣的问题:

在 1∼N1∼N 的某个排列中有多少个连号区间呢?

这里所说的连号区间的定义是:

如果区间 [L,R][L,R] 里的所有元素(即此排列的第 LL 个到第 RR 个元素)递增排序后能得到一个长度为 R−L+1R−L+1 的“连续”数列,则称这个区间连号区间。

当 NN 很小的时候,小明可以很快地算出答案,但是当 NN 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。

输入格式

第一行是一个正整数 NN,表示排列的规模。

第二行是 NN 个不同的数字 PiPi,表示这 NN 个数字的某一排列。

输出格式

输出一个整数,表示不同连号区间的数目。

数据范围

1≤N≤100001≤N≤10000,
1≤Pi≤N1≤Pi≤N

输入样例1:

4
3 2 4 1

输出样例1:

7

输入样例2:

5
3 4 2 5 1

输出样例2:

9

样例解释

第一个用例中,有 77 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4][1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]
第二个用例中,有 99 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[1,5],[2,2],[3,3],[4,4],[5,5]

分析:我们的想法一般就是枚举所有的区间去判断这是否是一个“连号区间”

判断:如果这是一个连号区间,那么就满足最大值和最小值的差就是区间的长度,那么满足差值为区间的长度是否就可以判断它是连号区间呢?可能有这样一组案例1144,最大值 - 最小值 == 2(区间长度),但是题目说明了 在 1∼N 的某个排列中有多少个连号区间,这是一个排列,所以不存在重复值,因此由最大最小值差来确定是否是 连号区间的方法是成立的

#include 
#include 
#include 

using namespace std;

const int N = 10010, inf = 1000000000;

int n, a[N];
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    
    
    int minx, maxx,fins = 0;
    for (int i = 0; i < n; i++) {		//区间的左端点
        minx = inf, maxx = - inf;
        for (int j = i; j < n; j++) {	//区间的右端点
            //判断maxx - minx是否是区间的长度
            minx = min (minx, a[j]);		//更新最值
            maxx = max (maxx, a[j]);
            if (maxx - minx == j - i) fins ++;
        }
    }
    cout << fins;
    return 0;
        
}

蓝桥杯:递增三元组

给定三个整数数组

A=[A1,A2,…AN]A=[A1,A2,…AN],
B=[B1,B2,…BN]B=[B1,B2,…BN],
C=[C1,C2,…CN]C=[C1,C2,…CN],

请你统计有多少个三元组 (i,j,k)(i,j,k) 满足:

  1. 1≤i,j,k≤N1≤i,j,k≤N
  2. Ai

输入格式

第一行包含一个整数 NN。

第二行包含 NN 个整数 A1,A2,…ANA1,A2,…AN。

第三行包含 NN 个整数 B1,B2,…BNB1,B2,…BN。

第四行包含 NN 个整数 C1,C2,…CNC1,C2,…CN。

输出格式

一个整数表示答案。

数据范围

1≤N≤1051≤N≤105,
0≤Ai,Bi,Ci≤1050≤Ai,Bi,Ci≤105

输入样例:

3
1 1 1
2 2 2
3 3 3

输出样例:

27
难度:中等
时/空限制:1s / 64MB
总通过数:4942
总尝试数:17100
来源:第九届蓝桥杯省赛C++B组,第九届蓝桥杯省赛JAVAB组
算法标签

1.前缀和思路

思路借鉴于yxc

分析:10^5最多是NlogN,我们枚举b,每一次是计算a中比b[i]小的数乘上c中比b[i]大的数

我们就可以提前计算出来,用cnt[a[i]]记录a[i]出现的次数,用s[]计算出前缀和 -》 s[value]就是比value小的数的个数的和,用来记录比b[i]大的数和比b[i]小的数

#include 
#include 
#include 
#include 

using namespace std;
typedef long long LL;

const int N = 100010;
int n;
int a[N], b[N], c[N];
int cnt[N], counta[N], countc[N], s[N];     
int main()
{
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i],a[i]++;
    for (int i = 0; i < n; i++) cin >> b[i],b[i]++;
    for (int i = 0; i < n; i++) cin >> c[i],c[i]++;
    
    //求counta[]
    for (int i = 0; i < n; i++) cnt[a[i]] ++;               //哈希数组记录a[i]出现了多少次
    for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];     //求cnt[]的前缀和
    for (int i = 0; i < n; i++) counta[i] = s[b[i] - 1];    //counta【b[i] - 1】代表比b[i]小的数的个数的和
    
    //求countc[],先把cnt和s数组清零
    memset(cnt, 0 ,sizeof cnt);memset(s, 0, sizeof s);
    for (int i = 0; i < n; i++) cnt[c[i]] ++;
    for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];
    for (int i = 0; i < n; i++) countc[i] = s[N - 1] - s[b[i]];     //全部的个数 - 小于等于b[i] 的个数 = 大于b[i]的个数
    
    
    LL fins = 0;
    for (int i = 0; i < n; ++i ) {
        fins += (LL)counta[i] * countc[i];
    }    
    cout << fins;
    return 0;
     
}

2.二分查找

比较正常的思路肯定是二分查找啦,直接上代码,用的是STL自带的lower_bound和upper_bound

如果不太明白的可以参考一下 这一篇文章

注意下面的得到的数是下标,而数组的下标从0开始,因此具体的个数要和下标的值区分好

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;
int n;
int a[N], b[N], c[N];
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];
    for (int i = 0; i < n; i++) cin >> c[i];
    
    
    sort(a, a + n); sort(b, b + n); sort(c, c + n);
    
    long long fins = 0;
    
    for (int i = 0; i < n; i++) {
        long long num1 = lower_bound(a, a + n, b[i]) - a;       //第一个大于等于b[i]的数的下标
        long long num2 = upper_bound(c, c + n, b[i]) - c;       //第一个大于b[i]的数的下标
        num2 = n - num2 ;
        fins += num1 * num2;
    }
    cout << fins;
    return 0;
}

手写二分,不确定暴力杯是否让使用上面的标准库的源码,保险亿点

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;
int n;
int a[N], b[N], c[N];
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];
    for (int i = 0; i < n; i++) cin >> c[i];
    
    
    sort(a, a + n); sort(b, b + n); sort(c, c + n);
    
    long long fins = 0;
    
    for (int i = 0; i < n; ++i) {
        
        int left = 0, right = n - 1, mid, value = b[i];    
        while (left < right) {
            //找小于b[i]的数的最大的下标
            int mid = (left + right + 1) >> 1;
            if (a[mid] >= value) {
                right = mid - 1;
            }else {
                left = mid;
            }
        }
        
        int num1 = left;            //确定好了num1
        if(a[num1] >= value)
            num1 = - 1;
        
        //下面查找大于b[i]的最小的值
        left = 0, right = n - 1;
        while (left < right ) {
            int mid = (left + right) >> 1;
            if (c[mid] > value) {
                right = mid;
            }else left = mid + 1;
        }
        int num2 = right;
        
        if(c[num2] <= value)
            num2 = n;
        
        num2 = n - num2 ;
        fins += 1ll * (num1 + 1) * num2;
    }
    cout << fins;
    
    return 0;
}

蓝桥杯:特别数的和(模拟)

小明对数位中含有 2、0、1、92、0、1、9 的数字很感兴趣(不包括前导 00),在 11 到 4040 中这样的数包括 1、2、9、101、2、9、10 至 32、3932、39 和 4040,共 2828 个,他们的和是 574574。

请问,在 11 到 nn 中,所有这样的数的和是多少?

输入格式

共一行,包含一个整数 nn。

输出格式

共一行,包含一个整数,表示满足条件的数的和。

数据范围

1≤n≤100001≤n≤10000

输入样例:

40

输出样例:

574

还是比较符合圈钱杯的题目类型的哇,比较简单的模拟题目,写一个函数判断每一个位是否是匹配2 0 1 9这四个数

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;
int n; 
long long fins;
int judge(int a) {
    int value = a;
    while (value) {
        int x = value % 10;
        if (x == 0 || x == 1 || x== 9 || x == 2) return a;
        value /= 10;
    }
    return 0;       //全部位数都判断完了,都不匹配,返回0
}


int main()
{
    cin >> n ;
    for (int i = 1; i <= n; i++) {
        fins += judge(i);
    }
    cout << fins;
    return 0;
}


蓝桥杯:错误票据

某涉密单位下发了某种票据,并要在年终全部收回。

每张票据有唯一的ID号。

全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。

因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。

你的任务是通过编程,找出断号的ID和重号的ID。

假设断号不可能发生在最大和最小号。

输入格式

第一行包含整数 NN,表示后面共有 NN 行数据。

接下来 NN 行,每行包含空格分开的若干个(不大于100个)正整数(不大于100000),每个整数代表一个ID号。

输出格式

要求程序输出1行,含两个整数 m,nm,n,用空格分隔。

其中,mm表示断号ID,nn表示重号ID。

数据范围

1≤N≤1001≤N≤100

输入样例:

2
5 6 8 11 9 
10 12 9

输出样例:

7 9

分析:很恶心啊,它输入的时候并不告诉你输入几个数,如果没有接触到stringstream可能会懵掉。或者比较呆呆的直接忽略掉n,剩下的我就直接while(cin…),:)其实这样真的是可以的哈哈,注意不要while (scanf…) scanf读到文件结束符号返回的是-1 ,不是0

那么我们就用stringstream进行一行数据的读取,不了解的可以点击这里和这里

#include 
#include 
#include 
#include 
using namespace std;


const int N = 10011;
int number = 0;
int a[N], res1 ,res2;
string t, line;
int main()
{
    int n;
    cin >> n;
    getline(cin, line);         //吃掉回车
    
    while (n --) {
        getline(cin, line);
        stringstream ss(line);      //stringstream流,用来读取一整行的字符串,可以将字符串中的空格过滤掉
        while (ss >> a[number]) {           //将流重定向到t中,每一次以空格为分隔
            number++;
        }
    }
    
    sort(a, a + number);
    for (int i = 1; i < number; ++i) {
        if (a[i] == a[i - 1]) res2 = a[i - 1];          //重复的值
        else if (a[i] - a[i - 1] >= 2) res1 = a[i] - 1;     //缺掉的号
    }
    cout << res1 << ' ' << res2;
    return 0;
    
}

NOIP2016普及组:回文日期

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 88 位数字表示一个日期,其中,前 44 位代表年份,接下来 22 位代表月份,最后 22 位代表日期。

显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 88 位数字是回文的。

现在,牛牛想知道:在他指定的两个日期之间(包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 88 位数字是回文的,当且仅当对于所有的 ii(1≤i≤81≤i≤8) 从左向右数的第 ii 个数字和第 9−i9−i 个数字(即从右向左数的第 ii 个数字)是相同的。

例如:

  • 对于 20162016 年 1111 月 1919 日,用 88 位数字 2016111920161119 表示,它不是回文的。
  • 对于 20102010 年 11 月 22 日,用 88 位数字 2010010220100102 表示,它是回文的。
  • 对于 20102010 年 1010 月 22 日,用 88 位数字 2010100220101002 表示,它不是回文的。

输入格式

输入包括两行,每行包括一个 88 位数字。

第一行表示牛牛指定的起始日期 date1date1,第二行表示牛牛指定的终止日期 date2date2。保证 date1date1 和 date2date2 都是真实存在的日期,且年份部分一定为 44 位数字,且首位数字不为 00。

保证 date1date1 一定不晚于 date2date2。

输出格式

输出共一行,包含一个整数,表示在 date1date1 和 date2date2 之间,有多少个日期是回文的。

输入样例:

20110101
20111231

输出样例:

1

分析:给了起始时间和末尾时间我们可能想到的一种方法就是,枚举所有的日期,并且判断是否是回文日期。但是对于日期的表示很麻烦,比如说你的1月应该表示为01。那我们不妨枚举所有的回文日期,判断它能否表示为一个日期,因为回文串是对称的,因此我们枚举四位就行了

#include 
#include 
#include 

using namespace std;

const int N = 10010;
int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 

int data1, data2;
int fins;


bool judge(int x) {     //传进来的都是回文串,判断这个是否是符合日期的
    int year = x / 10000;
    int month = x % 10000 / 100;
    int day = x % 100;
    if (month <= 0 || month > 12) return false;     //先不考虑二月的情况
    if (day <= 0 || (month != 2 && day > days[month]))return false ;
    
    int leap = year % 100 && year % 4 == 0 || year % 400 == 0;
    if (month == 2) {
        if (day > 28 + leap)return false;
    }
    return true;
}
int main()
{
    cin >> data1 >> data2;
    //枚举回文串
    for (int i = 1000; i <= 9999; i++) {
        int data = i, t = i;
        //构造成为回文串
        for (int j = 0; j < 4; j++) data = data * 10 + t % 10, t /= 10;
        if (data1 <= data && data <= data2 && judge(data)) fins ++;
    }
    cout << fins;
    return 0;
}

归并排序板子

给定你一个长度为 nn 的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 nn。

第二行包含 nn 个整数(所有整数均在 1∼1091∼109 范围内),表示整个数列。

输出格式

输出共一行,包含 nn 个整数,表示排好序的数列。

数据范围

1≤n≤1000001≤n≤100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5

不明白原理的可以百度或者点击这里

下面的代码比邓俊辉老师的弱一点,但是我觉得更好理解,大家请自取

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;

int n, a[N];

void merge(int *a, int start, int mid, int end) {
	int *tmp = new int[end - start + 1];		//开辟与数组同大小的暂存区,也可以开辟的[low,mid]
	int i = start;			//C数组 
	int j = mid + 1;		//B数组 
	int k = 0;				//A数组
	
	while (i <= mid && j <= end) {
		//谁小就填充
		tmp[k++] = (a[i] <= a[j]) ? a[i++] : a[j++]; 
	} 
	while (i <= mid) tmp[k++] = a[i++];
	while (j <= end) tmp[k++] = a[j++];
	
	//将数组返回到a中
	for (int i = 0; i < k; i++) {
		a[start + i] = tmp[i];
	} 
	delete [] tmp;
	 
}

void mergeSort(int *a, int start, int end) {
	if (a == NULL || start >= end) return ;
	
	int mid = (end + start) >> 1;
	mergeSort(a, start, mid);
	mergeSort(a, mid + 1, end);
	
	merge(a, start, mid, end);
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    mergeSort(a, 0, n - 1);		// 归并排序
    for (int i = 0; i < n; ++i ) cout << a[i] << " ";
    return 0;
}

蓝桥杯:移动距离

X星球居民小区的楼房全是一样的,并且按矩阵样式排列。

其楼房的编号为 1,2,3…1,2,3…

当排满一行时,从下一行相邻的楼往反方向排号。

比如:当小区排号宽度为 66 时,开始情形如下:

1  2  3  4  5  6
12 11 10 9  8  7
13 14 15 .....

我们的问题是:已知了两个楼号 mm 和 nn,需要求出它们之间的最短移动距离(不能斜线方向移动)。

输入格式

输入共一行,包含三个整数 w,m,nw,m,n,ww 为排号宽度,m,nm,n 为待计算的楼号。

输出格式

输出一个整数,表示 m,nm,n 两楼间最短移动距离。

数据范围

1≤w,m,n≤100001≤w,m,n≤10000,

输入样例:

6 8 2

输出样例:

4

分析:确定num1,num2的行号列号进行绝对值相减就行了

行号:数值不是从0开始而是从1开始进行排列的,那我们的行号就是(num - 1) / height 行号会出现0,列号是从1开始的,我们求的是差值,就不要在意是否和矩阵中的行列是否匹配了

列号:行号是偶数的话就是从左向右排列,直接取模,特判模为0

​ 行号是奇数的话就用height减一下就是了

#include 
#include 
#include 
#include 
using namespace std;

const int N = 10010;

int height, num1, num2;
int main()
{
    cin >> height >> num1 >> num2;
    //就是求出来num1和num2的行号和列号
    int r1, r2, c1, c2;
    r1 = (num1 - 1) / height; r2 = (num2 - 1) / height;
    if (r1 % 2 == 0) {
        c1 = num1 % height;
        c1 = (c1 == 0) ? height : c1;
    }else {
        c1 = height - (num1 % height) + 1;
    } 
    
    if (r2 % 2 == 0) {
        c2 = num2 % height;
        c2 = (c2 == 0) ? height : c2;
    }else {
        c2 = height - (num2 % height) + 1;
    } 
    
    //cout << r1 << " " << r2 << " " << c1 << " " << c2 << endl;
    int fins = abs(r1 - r2) + abs(c1 - c2);
    cout << fins;
    return 0;
}

蓝桥杯:日期问题

小明正在整理一批历史文献。这些历史文献中出现了很多日期。

小明知道这些日期都在1960年1月1日至2059年12月31日。

令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。

更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入格式

一个日期,格式是”AA/BB/CC”。

即每个’/’隔开的部分由两个 0-9 之间的数字(不一定相同)组成。

输出格式

输出若干个不相同的日期,每个日期一行,格式是”yyyy-MM-dd”。

多个日期按从早到晚排列。

数据范围

0≤A,B,C≤90≤A,B,C≤9

输入样例:

02/03/04

输出样例:

2002-03-04
2004-02-03
2004-03-02

同上面的日期回文串的问题类似,我们可以枚举所有的数,判断是不是一个日期,如果是日期的话看一下是否能够写成输入的格式,这样的好处是不用处理升序的格式和重复的问题了

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;
int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int a, b, c;

bool judge(int year, int month, int day){
    //年份肯定是可以的
    if (month == 0 || month > 12) return false;
    if (day == 0 || (month != 2 && day > days[month])) return false;
    if (month == 2) {
        int leap = (year % 100 && year % 4 == 0) || year % 400 == 0;      //闰年为1,不是闰年为0
        if (day > 28 + leap) return false;
    }
    return true;
}

int main()
{
    
    scanf ("%d/%d/%d", &a, &b, &c);     //用scanf进行输入的格式控制
    for (int i = 19600101; i <= 20591231; ++i) {
        //判断所有的数是不是正确的日期
        //判断对应的日期是不是能够表示成为输入的个数
        int year = i / 10000; int month = i % 10000 / 100; int day = i % 100;
        if (judge(year, month, day))
        {
            if (year % 100 == a && month == b && day == c ||        // 年/月/日
                month == a && day == b && year % 100 == c ||        // 月/日/年
                day == a && month == b &&year % 100 == c)           // 日/月/年
                printf("%d-%02d-%02d\n", year, month, day);
        }
    } 
    
}

洛谷:帮贡排序

题目背景

在absi2011的帮派里,死号偏多。现在absi2011和帮主等人联合决定,要清除一些死号,加进一些新号,同时还要鼓励帮贡多的人,对帮派进行一番休整。

题目描述

目前帮派内共最多有一位帮主,两位副帮主,两位护法,四位长老,七位堂主,二十五名精英,帮众若干。

现在absi2011要对帮派内几乎所有人的职位全部调整一番。他发现这是个很难的事情。于是要求你帮他调整。

他给你每个人的以下数据:

他的名字(长度不会超过30),他的原来职位,他的帮贡,他的等级。

他要给帮贡最多的护法的职位,其次长老,以此类推。

可是,乐斗的显示并不按帮贡排序而按职位和等级排序。

他要你求出最后乐斗显示的列表(在他调整过职位后):职位第一关键字,等级第二关键字。

注意:absi2011无权调整帮主、副帮主的职位,包括他自己的(这不是废话么…)

他按原来的顺序给你(所以,等级相同的,原来靠前的现在也要靠前,因为经验高低的原因,但此处为了简单点省去经验。)

输入格式

第一行一个数n,表示星月家园内帮友的人数。

下面n行每行两个字符串两个整数,表示每个人的名字、职位、帮贡、等级。

输出格式

一共输出n行,每行包括排序后乐斗显示的名字、职位、等级。

输入输出样例

输入 #1复制

9
DrangonflyKang BangZhu 100000 66
RenZaiJiangHu FuBangZhu 80000 60
absi2011 FuBangZhu 90000 60
BingQiLingDeYanLei HuFa 89000 58
Lcey HuFa 30000 49
BangYou3 ZhangLao 1000 1
BangYou1 TangZhu 100 40
BangYou2 JingYing 40000 10
BangYou4 BangZhong 400 1

输出 #1复制

DrangonflyKang BangZhu 66
RenZaiJiangHu FuBangZhu 60
absi2011 FuBangZhu 60
BingQiLingDeYanLei HuFa 58
BangYou2 HuFa 10
Lcey ZhangLao 49
BangYou1 ZhangLao 40
BangYou3 ZhangLao 1
BangYou4 ZhangLao 1

说明/提示

各种职位用汉语拼音代替。

如果职位剩1个,而有2个帮贡相同的人,则选择原来在前的现在当选此职位。

另: 帮派名号:星月家园

帮主尊号:Dragonfly Kang

帮派ID:2685023

帮派等级:4

帮派人数:101/110

帮派技能:

星月家园资料,欢迎各位豆油加入_

【数据范围】

对于10%的数据,保证n=3

对于40%的数据,保证各个人的帮贡均为0

对于100%的数据,保证3<=n<=110,各个名字长度<=30,0<=各个人的帮贡<=1000000000

1<=各个人等级<=150

保证职位必定为BangZhu,FuBangZhu,HuFa,ZhangLao,TangZhu,JingYing,BangZhong之间一个

保证有一名帮主,保证有两名副帮主,保证有一名副帮主叫absi2011

不保证一开始帮派里所有职位都是满人的,但排序后分配职务请先分配高级职位。例如原来设一名护法现在设两名。

保证名字不重复。

【题目来源】

fight.pet.qq.com

absi2011授权题目

#include
#include
using namespace std;
struct Node{
	string name;
	string zhiwei1,zhiwei2;    //换之前的职位和换之后的职位
	int banggong;
	int dengji;
	int shunxu;                  //输入的顺序  
	int zhi;	//代表转换后职位对应的数字 
};
Node node[120];
bool cmp1(Node a,Node b)        //第一次排序 帮贡相同 顺序小的优先
{
	if(a.banggong==b.banggong)
	return a.shunxu<b.shunxu;
	else return a.banggong>b.banggong;
}
bool cmp2(Node a,Node b)        //第二次排序 职位优先 然后是等级 然后是顺序
{
	if(a.zhi==b.zhi)
	{
		if(a.dengji==b.dengji)
		{
			return a.shunxu<b.shunxu;
		}
		return a.dengji>b.dengji; 
	}
	return a.zhi<b.zhi;
}
int main()
{
	int n,count2=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>node[i].name>>node[i].zhiwei1>>node[i].banggong>>node[i].dengji;
		node[i].shunxu=i;
	}
	sort(node+4,node+n+1,cmp1);	//由于不能改变帮主 副帮主 所以从第四个开始排序
	node[1].zhiwei2=node[1].zhiwei1;
	node[2].zhiwei2=node[2].zhiwei1;        //帮主和副帮主职位不变
	node[3].zhiwei2=node[3].zhiwei1; 
	for(int i=4;i<=n;i++)
	{
		if(i==4||i==5)
		node[i].zhiwei2="HuFa";
		else if(i>=6&&i<=9)
		node[i].zhiwei2="ZhangLao";
		else if(i>=10&&i<=16)
		node[i].zhiwei2="TangZhu";
		else if(i>=17&&i<=41)
		node[i].zhiwei2="JingYing";
		else node[i].zhiwei2="BangZhong";
	} 
	for(int i=1;i<=n;i++)
	{
		if(node[i].zhiwei2=="BangZhu")
		node[i].zhi=0;
		else if(node[i].zhiwei2=="FuBangZhu")
		node[i].zhi=1;
		else if(node[i].zhiwei2=="HuFa")
		node[i].zhi=2;
		else if(node[i].zhiwei2=="ZhangLao")
		node[i].zhi=3;
		else if(node[i].zhiwei2=="TangZhu")
		node[i].zhi=4;
		else if(node[i].zhiwei2=="JingYing")
		node[i].zhi=5;
		else if(node[i].zhiwei2=="BangZhong")
		node[i].zhi=6;
	}
	sort(node+1,node+1+n,cmp2);
	for(int i=1;i<=n;i++)
	{
		cout<<node[i].name<<" "<<node[i].zhiwei2<<" "<<node[i].dengji<<endl;
	}
}

以后会对于模拟排序题目进行补充的,最后希望大家在蓝桥杯都能够取得好的成绩!

你可能感兴趣的:(蓝桥杯,蓝桥杯,算法,职场和发展)