「PAT乙级真题解析」Basic Level 1055 集体照 (问题分析+完整步骤+伪代码描述+提交通过代码)

题设要求将给定的N个人按要求排成K排, 由于有明确列出要求, 所以我们初步判断这是一道按照要求翻译成代码的模拟题。 由于身高和名称是绑定关系, 排序用的是身高, 输出用的是名称, 之前分析其他题目的时候说过, 这种时候要将绑定的信息存储为类键值对结构, 放在一起, C语言中这意味着结构体。

#算法#数据结构#需求分析#pat考试#c语言

Table of Contents

乙级的题目训练主要用来熟悉编程语言的语法和形成良好的编码习惯和编码规范。从小白开始逐步掌握用编程解决问题。

PAT乙级BasicLevelPractice 1055

问题分析

题设要求将给定的N个人按要求排成K排, 由于有明确列出要求, 所以我们初步判断这是一道按照要求翻译成代码的模拟题。 要求如下:

  • 按照公式N/K(向下取整)计算每排人数, 多出来的人全部站在最后一排.
  • 后排所有人的各自都不比前排任何人矮;(这意味着需要排序, 根据样例输出, 高个子在第一行, 所以是降序排列)
  • 每排中最高者站中间, 并明确给出(中间位置为 m/2+1,其中 m 为该排人数,除法向下取整)
  • 每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧
    • 这需要提取一个方法来模拟这个排序方式
  • 若多人身高相同,则按名字的字典序升序排列。题设保证无重名

由于身高和名称是绑定关系, 排序用的是身高, 输出用的是名称, 之前分析其他题目的时候说过, 这种时候要将绑定的信息存储为类键值对结构, 放在一起, C语言中这意味着结构体。

模拟每排人员的左右交替站位

我们可以将左右交替的过程描述为如下:

  • 让中间人站定,
  • 让下一个人站在中间人左边个位置上
  • 让下一个人站在中间人右边个位置上
  • 让下一个人站在中间人左边个位置上
  • 让下一个人站在中间人右边个位置上
  • 让下一个人站在中间人左边个位置上
  • 让下一个人站在中间人右边个位置上 ... 我们可以看到变化的是方向(左和右)以及距离中间人位置的距离(1, 2, 3) 所以我们用sign存储方向, -1代表左, 1代表右, 用distance存储与中间人的距离. 由于"一左一右"是一个循环, 对应一个距离值, 所以在sign为-1时, 将distance加一, 代表进行新一轮的左右交替

完整描述步骤

  1. 获取输入: 人数, 排数, 每个人的姓名和身高
  2. 将所有人按照身高降序排列, 若多人身高相同,则按名字的字典序升序排列
  3. 先排最后一排:
    • 计算最后一排人数 A
    • 将排序的人员的前A个进行排队(输出)
  4. 计算其他排每排人数 B
  5. 排列其他排:
    • 将剩下的人员, 按照B人一组进行排队(输出)

伪代码描述

  1. get input: person_amount, row_amount, name and height of people
  2. sort people by heigth descendingly
    • if height is same, sort by name in alphabet order
  3. sort last row:
    • calculate person_amount_in_last_row = person_amount - (amount_one_row * row_amount) + amount_one_row;
    • start_index = 0, end_index = start_index + person_amount_in_last_row
    • print_positions(people, start_index, end_index)
  4. calculate amount_one_row = person_amount / row_amount
  5. sort rest rows:
    • do following steps (row_amount - 1) times:
      • start_index = end_index, end_index = start_index + amount_one_row
      • print_positions(people, start_index, end_index)

完整提交代码

/*
# 问题分析
题设要求将给定的N个人按要求排成K排, 
由于有明确列出要求, 所以我们初步判断这是一道按照要求翻译成代码的模拟题。
要求如下:
- 按照公式N/K(向下取整)计算每排人数, 多出来的人全部站在最后一排.
- 后排所有人的各自都不比前排任何人矮;(这意味着需要排序, 根据样例输出, 高个子在第一行, 所以是降序排列)
- 每排中最高者站中间, 并明确给出(中间位置为 m/2+1,其中 m 为该排人数,除法向下取整)
- 每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧
    - 这需要提取一个方法来模拟这个排序方式
- 若多人身高相同,则按名字的字典序升序排列。题设保证无重名
 
由于身高和名称是绑定关系, 排序用的是身高, 输出用的是名称,
之前分析其他题目的时候说过, 这种时候要将绑定的信息存储为类键值对结构, 放在一起, C语言中这意味着结构体。
 
 
# 模拟每排人员的左右交替站位
我们可以将左右交替的过程描述为如下:
- 让中间人站定,
- 让下一个人站在中间人<font color='ffafcc'>**左边**</font>第<font color='ee9b00'>**一**</font>个位置上
- 让下一个人站在中间人<font color='a2d2ff'>**右边**</font>第<font color='ee9b00'>**一**</font>个位置上
- 让下一个人站在中间人<font color='ffafcc'>**左边**</font>第<font color='ee9b00'>**二**</font>个位置上
- 让下一个人站在中间人<font color='a2d2ff'>**右边**</font>第<font color='ee9b00'>**二**</font>个位置上
- 让下一个人站在中间人<font color='ffafcc'>**左边**</font>第<font color='ee9b00'>**三**</font>个位置上
- 让下一个人站在中间人<font color='a2d2ff'>**右边**</font>第<font color='ee9b00'>**三**</font>个位置上
...
我们可以看到变化的是方向(左和右)以及距离中间人位置的距离(1, 2, 3)
所以我们用sign存储方向, -1代表左, 1代表右, 用distance存储与中间人的距离.
由于"一左一右"是一个循环, 对应一个距离值, 所以在sign为-1时, 将distance加一, 代表进行新一轮的左右交替
 
# 完整描述步骤
1. 获取输入: 人数, 排数, 每个人的姓名和身高
2. 将所有人按照身高降序排列, 若多人身高相同,则按名字的字典序升序排列
3. 先排最后一排:
    - 计算最后一排人数 A
    - 将排序的人员的前A个进行排队(输出)
4. 计算其他排每排人数 B
5. 排列其他排:
    - 将剩下的人员, 按照B人一组进行排队(输出)
 
# 伪代码描述
1. get input: person_amount, row_amount, name and height of people
2. sort people by heigth descendingly
    - if height is same, sort by name in alphabet order
3. sort last row:
    - calculate person_amount_in_last_row = person_amount - (amount_one_row * row_amount) + amount_one_row;
    - start_index = 0, end_index = start_index + person_amount_in_last_row
    - print_positions(people, start_index, end_index)
4. calculate amount_one_row = person_amount / row_amount
5. sort rest rows:
    - do following steps (row_amount - 1) times:
        - start_index = end_index, end_index = start_index + amount_one_row
        - print_positions(people, start_index, end_index)
*/
 
# include<stdio.h>
# include<string.h>
 
typedef struct
{
    char name[10];
    int height;
} person;
 
 
int cmp(const void * a, const void *b){
    person A = *(person*) a;
    person B = *(person*) b;
    return A.height != B.height ? B.height > A.height : strcmp(A.name, B.name);
}
 
void print_positions(person* people, int start_index, int end_index){
    int sign = 1;
    int distance = 0;
    int middle_index = (end_index - start_index) / 2;
    person row[end_index - start_index];
    for (int i = start_index; i < end_index; i++){
        row[middle_index+distance*sign] = people[i];
        sign = -sign;
        if (sign == -1){
            distance++;
        }
    }
 
    printf("%s", row[0].name);
    for (int i = 1; i < end_index - start_index; i++){
        printf(" %s", row[i].name);
    }
    printf("\n");
}
 
 
int main(){
    int person_amount, row_amount;
    scanf("%d %d", &person_amount, &row_amount);
 
    person people[person_amount];
    for (int i = 0; i < person_amount; i++){
        scanf("%s %d", people[i].name, &people[i].height);
    }
 
    qsort(people, person_amount, sizeof(person), cmp);
 
    int amount_one_row = person_amount / row_amount;
    int person_amount_in_last_row = person_amount - (amount_one_row * row_amount) + amount_one_row;
    int start_index = 0;
    print_positions(people, start_index, start_index + person_amount_in_last_row);
    start_index += person_amount_in_last_row;
 
    for (int i = 0; i < row_amount - 1; i++){
        print_positions(people, start_index, start_index + amount_one_row);
        start_index += amount_one_row;
    }
    return 0;
}
「PAT乙级真题解析」Basic Level 1055 集体照 (问题分析+完整步骤+伪代码描述+提交通过代码) | 生活糖果