PAT A1039&&A1047对比分析

今天做了两个练习不定长数组的问题,基于vector实现,但是很类似的两个提采用了截然不同的处理方法,值得深思。

A1039

  • 题目大意:

    • N个学生,K⻔课,给出选择每⻔课的学生姓名,最后对于给出的N个学生的姓名的选课情况进行询问,要求按顺序给出每个学生的选课情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFlT7c5S-1613402047976)(assets/1613401086404.png)]

  • 注意点

    • 学生名字对应选课代号可以用map<string,vector< Int> >,但本题数据量很大,不能用map,只能把学生名字转化成id,用hash存储。(对于本题来说,vector和map也只有这点差别)
    • 注意字母转化成int时的算法:n=n*26+str[i]-‘A’.
  • 代码

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int getid(char *name) { //将字符串转换成hash的算法
    int id = 0;
    for(int i = 0; i < 3; i++)
        id = 26 * id + (name[i] - 'A');
    id = id * 10 + (name[3] - '0');
    return id;
}
const int maxn = 26 * 26 * 26 * 10 + 10;
vector<int> v[maxn]; //用vector保存hashtable

int main() {
    int n, k, no, num, id = 0;
    char name[5];
    scanf("%d %d", &n, &k);
    for(int i = 0; i < k; i++) {
        scanf("%d %d", &no, &num);
        for(int j = 0; j < num; j++) {
            scanf("%s", name);
            id = getid(name);
            v[id].push_back(no); 
            //根据名字得到hash的key,在后面保存对应的课程号
        }
    }
    for(int i = 0; i < n; i++) {
        scanf("%s", name);
        id = getid(name);
        sort(v[id].begin(), v[id].end());
        printf("%s %lu", name, v[id].size());
        for(int j = 0; j < v[id].size(); j++)
            printf(" %d", v[id][j]);
        printf("\n");
    }
    return 0;
}

A1047

  • 题目大意
    • 和上题相反,给出选课人数和课程数目,给出每个学生的选课情况,让你根据课程编号依次输出选这门课的学生名字,按字母序排列。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c8tF7xE0-1613402047983)(assets/1613401225233.png)]

4、5的输出略

  • 注意点
    • 因为题目信息是按照学生名字给出的,则可以把学生设置成id号,另建char数组记录学生名字和id对应。
    • 设置vector< int >course [maxn] 数组,数组下标表示课程id,容器内部表示选当前这门课的学生id。
    • 题目要求按照字母序排列,可以自定义cmp函数,交换容器内部元素的位次,而不改变id和name的对应规则。
  • 代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int N,K;
int n,k=0;
char name[40010][5]; //用来存放名字,每个名字对应一个编号即数组的行号
vector<int> course[2510]; //记录选每门课的学生对应的编号
//对vector排序,输入参数是vector<type>的type类型参数
bool cmp(int c1,int c2){ //用strcmp进行字典序排序
    return strcmp(name[c1],name[c2])<0;
}
int main(){
    scanf("%d %d",&N,&K);
    for(int i=0;i<N;i++){
        scanf("%s %d",name[i],&n);
        for(int j=0;j<n;j++){
            scanf("%d",&k);
            course[k].push_back(i);
        }
        //sort不能放在这里,因为那个course没处理完,还可能再次改变
    }
    for(int i=1;i<=K;i++){
        printf("%d %d\n",i,int(course[i].size()));
        sort(course[i].begin(),course[i].end(),cmp);//在输出前按字典序排序
        for(int j=0;j<course[i].size();j++){
            printf("%s\n",name[course[i][j]]);
        }
    }
    return 0;
}

这两个题目相当类似,为什么A1039选择了将name对应的字符串转换成hash,而A1047利用数组下标对name进行编号呢?

这类题最难处理的就是如何将字符串转换成整数,因为转换成整数的话无论是数组下标直接访问,还是vector的访问,都能做到在O(1)时间找到对应的元素。事实上,hash的方法就是基于此思想。矛盾点在于,是直接用char二维数组保存name字符串集,得到这个下标,还是采用hash的方法,将每个字符串对应一个整数,再对应一个数组对应下标的位置。

关键在于,name这个字符串,更具体的说是全部选课的人的name的这个字符串集是什么时候全部得到的。

对于问题A1039,是根据选某门课程的人名输入的,也就是在输入阶段,如果不进行任何处理,我们是无法直接得到这个set的(name字符串集)。我们首先要明确,为了更短的处理时间,要尽可能一边读入数据一边处理数据。如果使用直接下标保存的方法,则每读入一次数据,现需要遍历一边检查这个name字符串是不是已经被保存在数组中了,最后得到的必须是一个没有重复元素的集合。从理论上讲也是行得通的。但是A1039采用hash的方法,使得每个name都对应唯一的数组下标,根本不会发生检查是否已经保存的问题。

而对于问题A1047,因为在一边读入的过程中,name字符串集已经得到,因此每到一个新的name直接保存在数组中并分配一个下标即可,无需检查。

p.s 关于A1039的分析在自己实际去尝试一下这个寻找的方法以后,发现了更本质的问题。即:最后output的时候,是根据下标输出字符串,还是根据字符串输出下标对应位置的区别。对于A1039,很明显input的最后一行给了字符串顺序,因此是根据字符串输出下标对应位置,这种时候只能采用hash的方法,将字符串作为key直接实现O(1)的查找输出;而对于A1047,是明显按12345的课程号顺序输出字符串,可以将课程号和下标联系起来。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值