《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序

pat A1012

单词:

Each input file contains one test case. 每个输入文件包含一个测试用例
corresponding 相应的
separated by a space 空格隔开
Hence 所以


题意:

输入:

1.输入n,m
2.输入n个学生的id C M E(学生编号,C语言成绩,数学成绩,英文成绩)
3.输入m个要查询的学生id

输出:

1.对应
2.输出学生编号,总排名,考场位置,数组在考场内的排名
3.排名原则:成绩不同,降序排列;否则,按照编号的升序排列


特殊技巧:排名的实现

分数不同的排名不同,分数相同但占用一个位置,比如5个学生分数分别为90 88 88 88 86 (注意成绩必须是倒叙排列)那么这5个学生排名是1 2 2 2 5

第一种方法:

从数组下标为0开始的排名为1,然后遍历其余部分;如果此人分数与前人相同,则排名不变;否则,排名为数组下标+1。

stu[0].r=1;
for(int i=1; i<n; i++)
{
	if(stu[i].score==stu[i-1].score)
		stu[i].r=stu[i-1].r
	else
		stu[i].r=i+1;
}

第二种方法:

有时题目不需要将排名记录下来,而是直接输出。可以令int类型的变量r为1,然后遍历所有个体;如果当前个体不是第一个且当前个体的分数不等于上一个个体的分数,则r=数组下标+1,此时r就是当前个体的排名。这样的做法适用于输出信息过多,导致第一种方法代码冗长。

int r=1;
for(int i=0; i<n; i++)
{
	if(i>0 && stu[i].score!=stu[i-1].score)
		r=i+1;
	cout << r;  //或者stu[i].r=r; 
}

解题思路:

1.由于要有总排名,考场排名…所以在定义结构时将这些内容加进去
2.cmp排序要求是:成绩不同,按照成绩降序排列,否则按照id的升序排列
3.主函数:
     1.输入信息,并在其考场内进行排序,按照书上第1中方法确定location_number和local_rank
     2.对整个结构数组排序,然后书上第2中方法,按要求输出

参考代码:

#include
#include
#include
#include
using namespace std;
/*
    解题思路:
    1.设置数组结构:6位整数学生id,数组grade表示学生的4个成绩
    2.设置字符数组course[4]按照优先级顺序ACME,设置二维数组:下标为学生编号,元素值为学生每一门科目所对应的排名。这样就可以通过找到排名最小(也就是成绩最大)的学生最擅长的科目去对应标签
    3.cmp表示按照成绩的降序排列
    4.main:
        1.输入成绩并计算出平均分
        2.对每一科目成绩先排名+按书上排名法1算出每个人每个科目对应的排名
        3.输入要查询的id,如果不存在,则输出NA;否则,在对应的id那一维数组中找出排名最小的,然后取对应course中的ACME标签
    5.这里有一个重要的思想:
        1.题目要求是先找出每个人CME课程中成绩最大的,并输出该科目对应拍名;如果成绩最大的不止1个,则其标签为A
        2.代码中的思想是直接对每个人的每个科目排名,找出排名最小的(对应成绩最大的)也就是学生最擅长的
    
*/
struct Student
{
    int id;  //存放6位整数的ID
    int grade[4];   //存放4个分数
}stu[2010];

char course[4]={'A','C','M','E'};   //按优先级顺序,方便输出
int Rank[10000000][4]={0};   //Rank[id][0]~Rank[id][4]为4门课对应的排名
int now;    //cmp函数中使用,表示当前按now号分数排序stu数组

bool cmp(Student a, Student b)   //stu数组按now号分数递减排序
{
    return a.grade[now] > b.grade[now];
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    //读入分数,其中grade[0]~grade[3]分别表示A,C,M, E;
    for(int i=0; i<n; i++)
    {
        scanf("%d%d%d%d", &stu[i].id,&stu[i].grade[1], &stu[i].grade[2], &stu[i].grade[3]);
        stu[i].grade[0]=(stu[i].grade[1] + stu[i].grade[2] + stu[i].grade[3])/3.0+0.5; //四舍五入算平均分
    }

    for(now=0; now<4; now++)  //对每一个人的每个成绩进行排名
    {
        //枚举A,C,M, E 4个中的一个
        sort(stu,stu+n,cmp);   //对所有考生按分数从大到小排序
        Rank[stu[0].id][now]=1;   //排序玩,将分数最高的设为rank1
        for(int i=1; i<n; i++)
        {
            //对于剩下的考生
            //若与前一位考生分数相同
            if(stu[i].grade[now] == stu[i-1].grade[now])
            {
                Rank[stu[i].id][now]=Rank[stu[i-1].id][now];  //则他们排名相同
            }
            else
            {
                Rank[stu[i].id][now]=i+1;   //否则,为其设置正确的排名
            }
        }
    }
    
    //输出内容
    int query;  //查询考生的id
    for(int i=0; i<m; i++)
    {
        scanf("%d", &query);
        if(Rank[query][0]==0)
        {
            //如果这个考生的id不存在,则输出n/a
            printf("N/A\n");

        }
        else
        {
            int k=0; //选出Rank[query][0~3]中最小的(rank值越小,排名越高)
            for(int j=0; j<4; j++)
            {
                if(Rank[query][j] < Rank[query][k])
                {
                    k=j;
                }
            }
            printf("%d %c\n",Rank[query][k],course[k]);
        }
    }
    return 0;
}


注意事项:

1.student结构中,字符数组char id[15],在用scanf输入时,不用在前面加取址符&

scanf("%s %d",stu[num].id,&stu[num].score);
  1. 用sort来对普通数组进行排序
sort(stu,stu+num,cmp);   //数组名 数组名+数组长度

知识总结:

1.strcmp(a,b)用来从左至右比较两个char型数组的字典序大小

#include
#include
using namespace std;
int main()
{
    char a[3];
    char b[3];
    scanf("%s %s",a,b);
    //当a的字典序列>b的字典序列时,返回正数

    //当a的字典序列

    //当a的字典序列=b的字典序列时,返回0

}


pat A1016

单词:

non-negative 非负的
integers 整数
a positive number 正数
is paired with 与…配对
chronologically 按年代次序地
guaranteed 保证
alphabetical 按字母顺序的
the lasting time 持续时间
the charge of the call 电话费


题意:

输入:

1.输入24个时间段所对应的每分钟多少美分(如下图所示)
《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第1张图片
2.输入n(表示共有n条通话记录)
3.输入具体记录包括名字(有20个单词组成)时间(月份,日期,几点,几分)字符串"on-line"或者“off-line”

输出:

1.输出顺序首先按照客户的姓名的字符串的升序排列和按照时间的顺序排列,排完后如下图所示
《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第2张图片
2.对于每一个人,有一个on-line和off-line想对应,如果on-line没有对应的off-line相对应,则忽视;同样off-line也是如此
3.对于每次输出先输出客户姓名和对应的月份,按照例子中的格式
4.对于每次的通话,输出开始时间和结束时间,格式按照日期,几点,几分,然后再输出每次通话的费用
5.最后输出总费用
对于电话费的计算方式如下图所示:
《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第3张图片


解题思路:

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第4张图片
代码中while嵌套的结构+第2层while之后的if判断

代码中第4层while后的if判断:

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第5张图片

参考代码:

#include
#include
#include
using namespace std;
/*
    解题思路:
    1.定义常量maxn为1010,收费时间表toll
    2.定义结构类型Record,及结构数组rec和结构变量temp:
        1.设置字符数组name
        2.设置变量month,dd,hh,mm
        3.将"on-line","off-line"表示为true和false
    3.设置cmp(按照名字的字典升序排列,如果名字相同,按照时间的升序排列)
    4.设置get_ans计算钱数:
        1.函数的参数有on,off,time,money其中time和money均初值为0
        2.temp表示on所在的结构
        3.while循环:
            1.time++计算一共的分钟数
            2.money累加toll[temp.hh](toll[temp.hh]表示temp.hh这个时间段内,每一分钟所对应多少美分)
            3.每次循环是按照1分钟累加,如果分钟数==60,或者小时数==24,则重置为0,且递增小时和天数
            4.循环的判断条件是 on的天数 小时 分钟 比 off的天数 小时 分钟 小即可
    5.main函数:
        1.通过循环输入每个时间点的收费放到数组toll中
        2.for循环输入姓名,month,dd,hh,mm
        3.然后输入临时变量line,如果为on-line则status为1;如果为off-line则status为0
        4.输入完之后排序
        5.while函数(每次循环单个处理所有用户信息)如下图所示
            1.第一个while(找到合适的next位置,因为后面根据next然后找到off循环输出对应信息):
                1.needPrint初值为0
                2.循环条件是next
const int maxn=1010;   //const修饰变量
int toll[25];   //资费,每个时间点对应的收费

struct Record
{
    char name[21];   //姓名,由于名字有20个字母所以要长度设为21
    int month,dd,hh,mm;         //月份,日,时,分
    bool status;              //status==true 表示该记录为on-line,否则为off-online
}rec[maxn],temp;

bool cmp(Record a, Record b)  //按照时间顺序排列
{
    int s=strcmp(a.name,b.name);
    if(s != 0)    //优先按姓名字典从小到大排序
        return s<0;
    else if(a.month != b.month)
        return a.month < b.month;        //按月份从小到大的排序
    else if(a.dd != b.dd)
        return a.dd<b.dd;          //按日期从小到大排序
    else if(a.hh != b.hh)
        return a.hh < b.hh;         //按小时从小到大排序
    else
        return a.mm < b.mm;       //按分钟从小到大
}

void get_ans(int on, int off, int& time, int& money)
{
    temp=rec[on];
    while(temp.dd < rec[off].dd || temp.hh < rec[off].hh || temp.mm < rec[off].mm)
    {
        time++;       //该次记录总时间加1mins
        money += toll[temp.hh];       //话费增加toll[temp.hh]
        temp.mm++;            //当前时间加1mins
        if(temp.mm == 60) //当前分钟数达到60
        {
            temp.mm=0;      //进入下一小时
            temp.hh++;
        }
        if(temp.hh == 24)         //当前小时数达到24
        {
            temp.hh=0;           //进入下一天
            temp.dd++;
        }
    }
}

int main()
{
    for(int i=0; i<24; i++)
    {
        scanf("%d",&toll[i]);        //资费
    }

    int n;
    scanf("%d", &n);      //记录数
    char line[10];   //临时存放on-line 或者 off-line的输入
    for(int i=0; i<n; i++)
    {
        scanf("%s %d:%d:%d:%d %s",rec[i].name,&rec[i].month,&rec[i].dd,&rec[i].hh,&rec[i].mm,line);
        if(strcmp(line,"on-line")==0)
        {
            rec[i].status = true;    //如果是on-line,则令status为true
        }
        else
        {
            rec[i].status=false;        //如果是off-line,则令status为false
        }
    }
    sort(rec,rec+n,cmp);      //排序
    int on=0,off,next;      //on和off为配对的两条记录,next为下一个用户
    while(on < n)         //每次循环处理单个用户所有记录
    {
        int needPrint=0;        //needPrint表示该用户是否需要输出
        next=on;            //从当前位置开始寻找下一个用户
        while(next < n && strcmp(rec[next].name, rec[on].name)==0)
        {
            if(needPrint ==0 && rec[next].status==true)
            {
                needPrint=1;       //找到on,置needPrint为1
            }
            else if(needPrint == 1 && rec[next].status == false)
            {
                needPrint=2;         //在on之后如果找到off,置needPrint为2
            }
            next++;
        }
        if(needPrint<2)     //没有找到配对的on-off
        {
            on = next;
            continue;
        }
        int AllMoney = 0;      //总共花费的钱
        printf("%s %02d\n",rec[on].name,rec[on].month);
        while(on < next)       //寻找该用户所有配对
        {
            while(on < next-1 && !(rec[on].status== true && rec[on+1].status==false))
            {           //必须是连续的on-line和off-line,因为不可能是on-line和off-line之间又有一个on-line不可能连续打了两次电话
                on++;       //直到找到连续的on-line和off-line
            }
            off=on+1;      //off必须是on的下一个
            if(off == next)        //已经输出完毕所有配对的on-line和off-line
            {
                on = next;
                break;
            }
            printf("%02d:%02d:%02d ",rec[on].dd,rec[on].hh,rec[on].mm);
            printf("%02d:%02d:%02d ",rec[off].dd,rec[off].hh,rec[off].mm);
            int time=0,money=0;     //时间,单词记录花费的钱
            get_ans(on,off,time,money);        //计算on到off内的时间和金钱
            AllMoney+=money;          //总金额加上该次记录的钱
            printf("%d $%.2f\n",time,money/100.0);
            on=off+1;         //完成一个配对,从off+1开始找下一对
        }
        printf("Total amount: $%.2f\n", AllMoney /100.0);
    }
    return 0;
}



知识总结:

1.const修饰变量,表示变量的值不能改变,将变量改为常量

#include
#include
#include
using namespace std;
const int maxn=1010;   //const修饰变量为常量
int toll[25];   //资费,每个时间点对应的收费

struct Record
{
    char name[21];   //姓名,由于名字有20个字母所以要长度设为21
    int month,dd,hh,mm;         //月份,日,时,分
    bool status;              //status==true 表示该记录为on-line,否则为off-online
}rec[maxn],temp; //这里数组的长度只能表示为常量

2.参数调用时的引用:

#include
#include
using namespace std;
int fun(int& a, int& b)     //使用引用来传递参数, a,b就是实参的别名,表示如果别名发生变化原来的也发生变化
{
    int temp;
    temp=a;
    a=b;
    b=temp;
    return 0;
}
int main()
{

    int a=20;
    int b=30;
    fun(a,b);         //直接调用时输出a,b
    cout << a << b;
    return 0;
}






pat A1028

题意:

输入:

1.输入整数n,c(其中n表示有n个学生,c表示值不同按照不同的顺序排列)
2.输入id(6位数字) 姓名(不超过8个字母)成绩

输出:

1.c == 1时,按照id的递增排序
2.c == 2时,按照按照姓名字典顺序的递增排序
3.c == 3时,按照成绩的递增顺序排列
4.当有学生的姓名和成绩相同时,按照id的递增顺序排列


解题思路:

在这里插入图片描述

参考代码:

#include
#include
#include
using namespace std;
/*
    解题思路:
    1.设置结构student,其中包括id,name的字符数组,grade
    2.设置3个cmp,分别对应题中的要求,注意当学生的姓名或者成绩相同时,按照id的递增顺序排列这个条件,应该在
    正常的排序之后
*/
struct student
{
    int id;
    char name[9];
    int grade;
};
bool cmp1(student a, student b)
{
    if(a.id!=b.id)
        return a.id<b.id;
    else if(a.name == b.name || a.grade == b.grade)
        return a.id < b.id;
}
bool cmp2(student a, student b)
{
    if(strcmp(a.name,b.name)!=0)
        return strcmp(a.name,b.name)<0;
    else if(a.name == b.name || a.grade == b.grade)
        return a.id < b.id;
}
bool cmp3(student a, student b)
{
    if(a.grade!=b.grade)
        return a.grade<b.grade;
    else if(a.name == b.name || a.grade == b.grade)
        return a.id < b.id;
}
int main()
{
    int n,c;
    cin >> n >> c;
    student v[n];
    for(int i=0; i<n; i++)
        cin >> v[i].id >> v[i].name >> v[i].grade;
    if(c==1)
        sort(v,v+n,cmp1);
    else if(c==2)
        sort(v,v+n,cmp2);
    else if(c==3)
        sort(v,v+n,cmp3);
    for(int i=0; i<n; i++)
    {
        printf("%06d %s %d\n",v[i].id,v[i].name,v[i].grade);
    }

    return 0;
}


pat A1055

题意:

输入:

1.输入n,m
2.n表示后面有n条记录,m表示有m个输出
3.输入内容有人名(不超过8个字母)年龄(200岁之内)财富
4.在输入 最多输出的人数 年龄范围的最小值 年龄范围的最大值

输出:

1.Case #i(i是从1开始一直m)
对于输入的n条记录,按照财富的降序排列,年龄的升序排列,如果有相同的年龄和财富,我们就按照名字字典的升序排列
如果没有找到对应的年龄范围的人,就输出none


解题思路:

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第6张图片

参考代码:

#include
#include
#include
using namespace std;
/*
    解题思路:
    1.设置结构:人名,年龄,财富
    2.设置排序cmp:按照财富的降序排列,按照年龄的升序排列,如果年龄和财富相等,按照名字字典的升序排列
    3.按照要求输入信息,然后排序
    4.注意下一这里:(题中要求,在每个输出个数的最大值中不超过100,也就是当人很多时,只要前100个)
        1.设置age数组表示每个年龄的人数,其数组成员初始值设为0
        2.循环遍历之前的输入数组v,如果这个年龄的人数小于100,则age[v[i].age]++,并且将原来数组中的结构放入到新的数组结构中
        3.如果不加入这个操作,就会在第3个测试点超时
    5.循环输入 要输出的人数,年龄范围
    6.在原来的结构数组中,循环查找满足年龄要求的,和在循环中设置temp,表示需要输出的人数,然后一共输出满足个数的人
    7.设置flag=0,如果没有满足年龄范围的,输出none
*/
int age[201]={0};        //统计每个年龄的人数
struct people
{
    char name[9];
    int age;
    int wealth;
};
bool cmp(people a, people b)
{
    if(a.wealth != b.wealth)
        return a.wealth > b.wealth;        //降序排列财富
    else if(a.age!=b.age)
        return a.age < b.age;         //增序排列年
    if(a.wealth == b.wealth && a.age == b.age)   //如果财富和名字都相同的话,就按照字母的递增顺序排列
        return strcmp(a.name,b.name)<0;
}
int main()
{
    int n,m,ss=0;
    cin >> n >> m;
    people v[n],newvector[n];
    for(int i=0; i<n; i++)
        cin >> v[i].name >> v[i].age >> v[i].wealth;
    sort(v,v+n,cmp);
    for(int i=0; i<n; i++)
    {
        if(age[v[i].age]<100)
        {
            age[v[i].age]++;
            newvector[ss++]=v[i];
        }
    }
    for(int i=0; i<m; i++)
    {
        int temp,amain,amax,flag=0;
        cin >> temp >> amain >> amax;
        cout << "Case #" << i+1 << ":" << endl;
        for(int k=0; k<ss; k++)
            if(amain<=newvector[k].age && newvector[k].age<=amax)
            {
                flag=1;
                if(temp==0)
                {
                    break;
                }
                cout << newvector[k].name << " " << newvector[k].age << " " << newvector[k].wealth << endl;
                temp--;
            }
        if(flag==0)
            cout << "None" << endl;
    }
}



《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第7张图片

#include
#include
#include
#include
using namespace std;
const int maxn = 10010;
/*
题目大意:
1.先按k道题所得总分从高到低排序
2.若总分相同,则按完美解决(即获得题目满分)的题目数量从高到低排序
3.若完美解决的题目数量也相同,则按准考生号从小到大排序
输出格式:
1.总分相等,排名相同
2.如果某位考生全场都没有提交记录,或者是没有能通过编译的提交,则该考生的信息不输出
3.对需要输出的考生,如果某到题没有能通过编译的提交,则将该题记为0分,如果某道题没有提交记录,则输出-

1、在这道题没有任何操作的情况下,最后输出-,-的意思是学生连这道题就没做,跟不要说提交了。因为只要提交获得的分数就是从-1~该题满分这个范围,如果得-1,0最后都输出0
2.如果这道题得分为0或者-1,则输出0(获得分数为-1,表示没有通过编译

解题思路:
1.程序一共分为4个部分
第一部分:
    1.定义一个结构Student:
        1.准考证号
        2.数组score[6],每道题得分,题目个数最大值为5,所以数组长度为6
        3.设置常量n,k,m
        4.设置全局变量full,长度也是6,因为题目的最多个数为5
    2.设置cmp:按照总分的降序排列,然后按照完美解决问题数的递减排列,最后按照编号的递增顺序排列
    3.初始化结构,对于每个用户的对应结构初始化:
        1.将id号设为i
        2.将总分设为0
        3.完美解题数也设为0
        4.初始化没有通过编译的提交
        5.按每个字节初始化,每个学生的结构中的每道题成绩设为-1
第2部分:
    1.主函数:
        1.输入n,k,m;初始化每个结构
        2.遍历学生的m个刷题记录(其中要设置4个内容):
            1.如果学生获得的分数不为-1,则可以最后输出该学生的成绩;如果不输入u_id p_id score,那么stu[u_id].flag肯定为false(从未提交过任何解决方案的人),,如果某个学生它获得的分数记录中只有-1,那么最后也不输出(这个表示从未提交过可以通过编译器的解决方案的人)。那当然只要该学生有提交过可以通过编译的(就是获得分数的)那么最后还是可以输出

            2.如果获得的分数第一次为-1,并且stu[u_id].score[p_id]之前的分数,则覆盖(这里包含几个意思:1.只会记录学生刷题记录中,每道题得分的最大值 2.如果该题目获得满分,则学生的结构中题目所对应的那个分数应该就是该题的满分值,这样在同一道题中再次获得满分,就不会记录在完美解题中
                    3.如果该学生的这道题得分为0分,由于初始值都是-1,0>-1 则最后谁得-1就输出0
第3部分:
    1.排序,然后(结合flag为true)按照排名的技巧进行排名
    2.如果该人有没有提交的,提交了的并且通过编译的(得上分的),也有提交了的但是未通过编译的,则对于此人输出时没有提交的输出-,提交了的并且获得分数的则输出该分数,没有通过编译的则输出0

*/
struct Student
{
int id; //准考证号
int score[6]; //每道题得分
bool flag; //能否有能通过编译的提交
int score_all; //总分
int solve; //完美解题数
}stu[maxn];
int n,k,m;
int full[6]; //每道题的满分
bool cmp(Student a, Student b)
{ //排序函数
if(a.score_all != b.score_all) //按照总分降序输出
return a.score_all > b.score_all;
else if(a.solve!=b.solve) //按照完美解题数降序输出
return a.solve > b.solve;
else
return a.id }
void init()
{
//初始化
for(int i=1; i<=n; i++)
{
stu[i].id=i; //准考证号记为i
stu[i].score_all = 0; //总分初始化为0
stu[i].solve=0; //完美解题数初始化为0
stu[i].flag=false; //初始化为没有能通过编译的提交
memset(stu[i].score,-1,sizeof(stu[i].score)); //题目得分记为-1,memset是以字节为单位,初始化内存块。也可以将sizeof(stu[i].score)设为24它是以字节位单位
//设置stu结构数组中的分数初始值为-1
}
}
int main()
{
scanf("%d%d%d",&n,&k,&m);
init(); //初始化结构
for(int i=1; i<=k; i++)
{
scanf("%d",&full[i]); //输入每道题的满分值
}
int u_id,p_id,score_obtained; //考生id,题目id及所获分值

for(int i=0; i满分值,则覆盖
        stu[u_id].flag=true;
        stu[u_id].score[p_id]=0;
         stu[u_id].solve++;
         stu[u_id].score[p_id]=score_obtained;
    */
    scanf("%d%d%d",&u_id,&p_id,&score_obtained); //输入id号,输入题号,输入获得分数

    if(score_obtained!=-1)   //若不是编译错误,则该考生有能通过编译的提交
        stu[u_id].flag=true;

    if(score_obtained == -1 && stu[u_id].score[p_id]==-1)  //如果获得的分数值为-1,
    {
        //某题第一次编译错误,分值记为0分,便与输出
        stu[u_id].score[p_id]=0;
    }

    if(score_obtained==full[p_id] && stu[u_id].score[p_id] stu[u_id].score[p_id])
    {
        stu[u_id].score[p_id]=score_obtained;       //某题获得更高分值,则覆盖
    }

}

for(int i=1; i<=n; i++) //计算每个学生满足要求的总分,方便后面排序
{
for(int j=1; j<=k; j++)
{
if(stu[i].score[j]!=-1)
{
//计算总分
stu[i].score_all+=stu[i].score[j];
}
}
}
sort(stu+1,stu+n+1,cmp); //按要求排序
int r=1; //当前排名
for(int i=1; i<=n && stu[i].flag==true; i++)
{
if(i>1 && stu[i].score_all != stu[i-1].score_all) //输出排名技巧
{
//当前考生分数低于前一位考生分数,则排名为在该考生之前的总考生分数
r=i;
}
printf("%d %05d %d",r,stu[i].id,stu[i].score_all);
for(int j=1; j<=k; j++)
{
if(stu[i].score[j] == -1)
{
printf(" -"); //没有过提交
}
else
{
printf(" %d",stu[i].score[j]);
}
}
printf("\n");
}
return 0;
}


pat A1075

单词:

ranklist 登记表
submissions 意见书,服从,柔和
compiler 编译器
partial 部分的
tie 不分胜负


题意:

这里是引用

输入:

1.输入n,k,m(分别表示学生人数 题目数量 刷题记录)
2.接下来m行表示具体的刷题记录(学生id(5位数字)题号 成绩)
3.第2行中,20表示第1题满分值;25表示第2题满分值,以此类推

输出:

1.排名取决于总分,总分相同者就会有相同的排名
2.如果用户对于某一题没有做任何的提交,则后面输出对应的位置应该输出-(这个说白了就是:这个人提交的题目中,有的题是获得了分数,而有的题是没有通过编译,或者压根没有做这道题,这些都输出-);如果1道题有多种提交,则只记录分值最大的
3.排名按照增序进行排列,如果用户有相同的排名则应该按照完美解题数来降序排列,如果在不分上下的话,就按照id的升序排列
4.对以哪些从来没有提交任何解决问题的人不要输出,以及对那些从来没有提交过能通过编译器的人,都不输出(这个里面,说白了就是某人的答题数为0,或者只有1个没有或者全部都是没有通过编译的提交)


解题思路:

1.程序一共分为4个部分
第一部分:
    1.定义一个结构Student:
        1.准考证号
        2.数组score[6],每道题得分,题目个数最大值为5,所以数组长度为6
        3.设置常量n,k,m
        4.设置全局变量full,长度也是6,因为题目的最多个数为5
    2.设置cmp:按照总分的降序排列,然后按照完美解决问题数的递减排列,最后按照编号的递增顺序排列
    3.初始化结构,对于每个用户的对应结构初始化:
        1.将id号设为i
        2.将总分设为0
        3.完美解题数也设为0
        4.初始化没有通过编译的提交
        5.按每个字节初始化,每个学生的结构中的每道题成绩设为-1
第2部分:
    1.主函数:
        1.输入n,k,m;初始化每个结构
        2.遍历学生的m个刷题记录(其中要设置4个内容):
            1.如果学生获得的分数不为-1,则可以最后输出该学生的成绩;如果不输入u_id p_id score,那么stu[u_id].flag肯定为false(从未提交过任何解决方案的人),,
                如果某个学生它获得的分数记录中只有-1,那么最后也不输出(这个表示从未提交过可以通过编译器的解决方案的人)。那当然只要该学生有提交过可以通过编译的(就是获得分数的)那么最后还是可以输出
            2.如果获得的分数第一次为-1,并且stu[u_id].score[p_id]之前的分数,则覆盖(这里包含几个意思:1.只会记录学生刷题记录中,每道题得分的最大值 2.如果该题目获得满分,则学生的结构中题目所对应的那个分数应该就是该题的满分值,这样在同一道题中再次获得满分,就不会记录在完美解题中
                    3.如果该学生的这道题得分为0分,由于初始值都是-1,0>-1 则最后谁得-1就输出0
第3部分:
    1.排序,然后(结合flag为true)按照排名的技巧进行排名
    2.如果该人有没有提交的,提交了的并且通过编译的(得上分的),也有提交了的但是未通过编译的,则对于此人输出时没有提交的输出-,提交了的并且获得分数的则输出该分数,没有通过编译的则输出0

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.1排序_第8张图片

参考代码:

#include
#include
#include
#include
using namespace std;
const int maxn = 10010;
/*
    题目大意:
    1.先按k道题所得总分从高到低排序
    2.若总分相同,则按完美解决(即获得题目满分)的题目数量从高到低排序
    3.若完美解决的题目数量也相同,则按准考生号从小到大排序
    输出格式:
    1.总分相等,排名相同
    2.如果某位考生全场都没有提交记录,或者是没有能通过编译的提交,则该考生的信息不输出
    3.对需要输出的考生,如果某到题没有能通过编译的提交,则将该题记为0分,如果某道题没有提交记录,则输出-

    1、在这道题没有任何操作的情况下,最后输出-,-的意思是学生连这道题就没做,跟不要说提交了。因为只要提交获得的分数就是从-1~该题满分这个范围,如果得-1,0最后都输出0
    2.如果这道题得分为0或者-1,则输出0(获得分数为-1,表示没有通过编译

    解题思路:
    1.程序一共分为4个部分
    第一部分:
        1.定义一个结构Student:
            1.准考证号
            2.数组score[6],每道题得分,题目个数最大值为5,所以数组长度为6
            3.设置常量n,k,m
            4.设置全局变量full,长度也是6,因为题目的最多个数为5
        2.设置cmp:按照总分的降序排列,然后按照完美解决问题数的递减排列,最后按照编号的递增顺序排列
        3.初始化结构,对于每个用户的对应结构初始化:
            1.将id号设为i
            2.将总分设为0
            3.完美解题数也设为0
            4.初始化没有通过编译的提交
            5.按每个字节初始化,每个学生的结构中的每道题成绩设为-1
    第2部分:
        1.主函数:
            1.输入n,k,m;初始化每个结构
            2.遍历学生的m个刷题记录(其中要设置4个内容):
                1.如果学生获得的分数不为-1,则可以最后输出该学生的成绩;如果不输入u_id p_id score,那么stu[u_id].flag肯定为false(从未提交过任何解决方案的人),,
                    如果某个学生它获得的分数记录中只有-1,那么最后也不输出(这个表示从未提交过可以通过编译器的解决方案的人)。那当然只要该学生有提交过可以通过编译的(就是获得分数的)那么最后还是可以输出
                2.如果获得的分数第一次为-1,并且stu[u_id].score[p_id]之前的分数,则覆盖(这里包含几个意思:1.只会记录学生刷题记录中,每道题得分的最大值 2.如果该题目获得满分,则学生的结构中题目所对应的那个分数应该就是该题的满分值,这样在同一道题中再次获得满分,就不会记录在完美解题中
                        3.如果该学生的这道题得分为0分,由于初始值都是-1,0>-1 则最后谁得-1就输出0
    第3部分:
        1.排序,然后(结合flag为true)按照排名的技巧进行排名
        2.如果该人有没有提交的,提交了的并且通过编译的(得上分的),也有提交了的但是未通过编译的,则对于此人输出时没有提交的输出-,提交了的并且获得分数的则输出该分数,没有通过编译的则输出0
*/
struct Student
{
    int id;   //准考证号
    int score[6];   //每道题得分
    bool flag;       //能否有能通过编译的提交
    int score_all;        //总分
    int solve;        //完美解题数
}stu[maxn];
int n,k,m;
int full[6];         //每道题的满分
bool cmp(Student a, Student b)
{       //排序函数
    if(a.score_all != b.score_all)        //按照总分降序输出
        return a.score_all > b.score_all;
    else if(a.solve!=b.solve)             //按照完美解题数降序输出
        return a.solve > b.solve;
    else
        return a.id<b.id;        //按照编号的升序输出
}
void init()
{
    //初始化
    for(int i=1; i<=n; i++)
    {
        stu[i].id=i;       //准考证号记为i,先设置id为i,先保证这里面有值,可以在后面
        stu[i].score_all = 0;          //总分初始化为0
        stu[i].solve=0;         //完美解题数初始化为0
        stu[i].flag=false;        //初始化为没有能通过编译的提交
        memset(stu[i].score,-1,sizeof(stu[i].score));              //题目得分记为-1,memset是以字节为单位,初始化内存块。也可以将sizeof(stu[i].score)设为24它是以字节位单位
        //设置stu结构数组中的分数初始值为-1
    }
}
int main()
{
    scanf("%d%d%d",&n,&k,&m);
    init();  //初始化结构
    for(int i=1; i<=k; i++)
    {
        scanf("%d",&full[i]);    //输入每道题的满分值
    }
    int u_id,p_id,score_obtained;      //考生id,题目id及所获分值

    for(int i=0; i<m; i++)         //一共有m个考生的记录,循环是按照m个记录来循环的
    {
        /*
            此循环在根据 输入的 用户id号,问题号id,每道题得分值
            1.如果没有获得-1分,则结构中flag设为1
            2.如果第1次获得-1分,则结构中对应该题目的值设为0,如果这道题再得-1分,就不管了
            3.如果这道题第1次得满分,则完美解题数目+1,可以一直统计下去
            4.如果获得的分数>满分值,则覆盖
            stu[u_id].flag=true;
            stu[u_id].score[p_id]=0;
             stu[u_id].solve++;
             stu[u_id].score[p_id]=score_obtained;
        */
        scanf("%d%d%d",&u_id,&p_id,&score_obtained); //输入id号,输入题号,输入获得分数

        if(score_obtained!=-1)   //若不是编译错误,则该考生有能通过编译的提交
            stu[u_id].flag=true;

        if(score_obtained == -1 && stu[u_id].score[p_id]==-1)  //如果获得的分数值为-1,
        {
            //某题第一次编译错误,分值记为0分,便与输出
            stu[u_id].score[p_id]=0;
        }

        if(score_obtained==full[p_id] && stu[u_id].score[p_id]!=full[p_id])
        {
            stu[u_id].solve++;        //某题第一次获得满分,则完美解题数加1
        }

        if(score_obtained > stu[u_id].score[p_id])
        {
            stu[u_id].score[p_id]=score_obtained;       //某题获得更高分值,则覆盖
        }
   }

   for(int i=1; i<=n; i++)   //计算每个学生满足要求的总分,方便后面排序
   {
       for(int j=1; j<=k; j++)
           if(stu[i].score[j]!=-1)
               //计算总分
               stu[i].score_all+=stu[i].score[j];

   }
   sort(stu+1,stu+n+1,cmp);         //按要求排序
   int r=1;           //当前排名
   for(int i=1; i<=n && stu[i].flag==true; i++)
   {
       if(i>1 && stu[i].score_all != stu[i-1].score_all)  //输出排名技巧
       {
           //当前考生分数低于前一位考生分数,则排名为在该考生之前的总考生分数
           r=i;
       }
       printf("%d %05d %d",r,stu[i].id,stu[i].score_all);
       for(int j=1; j<=k; j++)
       {
           if(stu[i].score[j] == -1)
           {
               printf(" -");        //没有过提交
           }
           else
           {
               printf(" %d",stu[i].score[j]);
           }
       }
       printf("\n");
   }
   return 0;
}


注意事项:

  1. :必须设置id为i,因为后面中就是直接利用v[u_id]=…,当u_id为3时,则直接赋值即可 //21行

2.这里要避免在同一道题中,连续2次获得满分值并且记录完美解题数如果,相同的题目再次获得满分值,就必须用v[i].fenshu[problem_id]是否不等于[problem_id]来判断,因为后面的代码设 //54行代码

注意3:排序时是从v+1开始排序,因为是从1开始记录刷题信息的 //67行


知识总结:

1。按照字节对于指定的数组进行赋初值

#include
#include   //必须加入头文件
using namespace std;
int main()
{
    int a[10];
    memset(a,-1,sizeof(a)); //数组名 初始值 数组a的字节
    for(int i=0; i<10; i++)
        cout << a[i] << " ";
}

pat A1083

单词:

interval n. 间隔;幕间休息;(数学)区间


题意:

输入:

1.输入n
2.输入n个学生的姓名(不超过10个字母) 学号(不超过10个字母) 成绩
3.输入区间:最小值 最大值

输出:

1.输出满足区间内且成绩按照降序排列
2.如果没有学生成绩在范围内,则输出NONE


解题思路1:

在这里插入图片描述
解题思路:
1.将所有成绩不在范围内的,全部设置为0(注意成绩的范围是0–100)
2.设置cmp按照成绩的降序排列
3.最后根据成绩如果不为0,则输出;否则,就统计不满足要求人数,然后根据统计出来的人数和总人数相比
如果相同,则输出NONE

参考代码:

#include
#include
using namespace std;
/*
    解题思路:
    1.将所有成绩不在范围内的,全部设置为0(注意成绩的范围是0--100)
    2.设置cmp按照成绩的降序排列
    3.最后根据成绩如果不为0,则输出;否则,就统计不满足要求人数,然后根据统计出来的人数和总人数相比
    如果相同,则输出NONE
*/
struct student
{
    char name[12];
    char id[12];
    int grade;
}List[100001];
int n,grademin,grademax;
void biaoji()
{
    for(int i=0; i<n; i++)
        if(List[i].grade<grademin || List[i].grade>grademax)
            List[i].grade=0;
}
bool cmp(student a, student b)
{
    if(a.grade!=b.grade)
        return a.grade > b.grade;
}
int main()
{
    int wsf=0;
    cin >> n;
    for(int i=0; i<n; i++)
        cin >> List[i].name >> List[i].id >> List[i].grade;
    cin >> grademin >> grademax;
    biaoji();
    sort(List,List+n,cmp);
    for(int i=0; i<n; i++)
    {
        if(List[i].grade!=0)
            cout << List[i].name << " " << List[i].id << endl;
        else
            wsf++;
    }
    if(wsf==n)
        cout << "NONE" << endl;
    return 0;
}


解题思路2:

在这里插入图片描述
解题思路:
1.先按照成绩的降序排列
2.先设置flag初始化为false,遍历循环数组,如果成绩在范围之内,则输出,并将flag设为true,最后,for循环结束
后,如果flag为false,则输出NONE

参考代码:

#include
#include
using namespace std;
/*

*/
struct student
{
    char name[12];
    char id[12];
    int grade;
}List[100001];
int n,grademin,grademax;
bool cmp(student a, student b)
{
    if(a.grade!=b.grade)
        return a.grade > b.grade;
}
int main()
{
    bool flag=false;
    cin >> n;
    for(int i=0; i<n; i++)
        cin >> List[i].name >> List[i].id >> List[i].grade;
    cin >> grademin >> grademax;
    sort(List,List+n,cmp);
    for(int i=0; i<n; i++)
        if(List[i].grade>=grademin && List[i].grade<=grademax)
        {
            cout << List[i].name << " " << List[i].id << endl;
            flag=true;
        }
    if(flag==false)
        cout << "NONE" << endl;
    return 0;
}


你可能感兴趣的:(《算法笔记上机实验指南》)