[PAT解题报告] Course List for Student

题目本身不难,给定一些课程,还有哪些学生选了这个课,最后查询某些学生上了哪些课。
本来就是一个图的邻接表输出的问题,但是PAT在这个问题上卡时间和卡内存非常紧——所以用了一些诡异的技巧。
(1) 学生id是3个大写字母加一个数字。所以我们可以把这4个字符作一个26进制数。
(2) 2500门课程,每个课程id在1-2500以内,所以我可以用12bit(0-4095)表示这个课程。把(1)的数左移动12位,低12位表示这门课程。
(3) 于是((1) << 12) | (2) 这个整数表示一条边,也就是(1)这个学生选了(2)这门课程。
(4) 把(3)里面这些整数从小到大排序,可以知道id相同的学生在一起了,同一个学生不同的课程id由小到大排好顺序了。
(5) 查询一个学生的时候,先在(4)的数组里二分出第一个该id学生出现的位置——也就是查询 >= (id << 12)的第一个位置,如果找到了x发现,发现x >> 12  == id,说明确实又这个学生的信息。
(6) 如果能找到这样的id,顺着这个id找下去就可以。但是为了提前知道个数,我们可以再二分一次,找到这个id最后一次出现的位置,然后顺着输出就可以了。

总之,这个题就是排序加二分的题目——把一个很简单的问题弄得复杂了。

代码:
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;


const int w[] = {1, 26, 26 * 26, 26 * 26 * 26};
int all[500002];
int total;
char s[10];

inline int make(char *s) {
    return (s[0] - 'A') * w[3] + (s[1] - 'A') * w[2] + (s[2] - 'A') * w[1] + (s[3] - '0'); 
}


int find1(int x) { // first >= x
int left = 0, right = total - 1, r = -1;
    while (left <= right) {
        int mid = (left + right) >> 1;
        if (all[mid] >= x) {
            r = mid;
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return r;
}
 
int find2(int left,int x) { //last <= x
int right = total - 1;
    ++left;
    while (left <= right) {
        int mid = (left + right) >> 1;
        if (all[mid] <= x) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    return left - 1;
}

int main() {
int n,m;
    scanf("%d%d",&n,&m);
    for (int i = 0;i < m;++i) {
        int x,y;
        scanf("%d%d",&x,&y);
        for (;y;--y) {
            scanf("%s",s);
            all[total++] = (make(s) << 12) | x;
        
        }
    }
    sort(all, all + total);
    for (;n;--n) {
        scanf("%s",s);
        printf("%s",s);
        int x = make(s), y = find1(x << 12);
        if ((y < 0) || ((all[y] >> 12) > x)) {
            puts(" 0");
        }
        else {
            x = find2(y, (x << 12) | m);
            printf(" %d",x - y + 1);
            for (;y <= x; ++y) {
                printf(" %d",all[y] & 4095);
            }
            puts("");
        }
    }
}

原题链接: http://www.patest.cn/contests/pat-a-practise/1039

全部评论
看不懂上面的文字思路,我跨考的,这正常吗
点赞 回复 分享
发布于 2018-09-04 00:02
佩服就完事了
点赞 回复 分享
发布于 2019-08-09 10:42
建议看不懂的这个版本的童鞋搜搜 柳婼大神的,她的代码好懂很多
点赞 回复 分享
发布于 2019-08-09 10:47

相关推荐

我朋友的华子2012,HR已经开始问意向地区了,好急
不讲武德的黑眼圈很能干:急得不行 也不说评级 不知道报的多少啊😡
点赞 评论 收藏
分享
牛客963010790号:为什么还要收藏
点赞 评论 收藏
分享
joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务