跳转至

计算机类课程

(211G0290)计算机科学基础(A)

  • 考核方式:25%小测+35%作业(平时作业/论文)+40%期末
  • 这门课在22级开始教改,23级继续教改,24级大约是取消了😄。
  • 虽然不知道22级以前是咋样的,不过22级教改之后,这门课的内容变得杂乱繁多,包括很多计算机偏底层的知识(编码/系统/硬件),office等软件的使用和许多python内容。 高中学过技术的大概对python会熟悉一些,但是python真不难,背背语法就能拿分。反倒是基础知识类因为考试范围不明确要难一些。
  • 期末复习可以参考bhh的ppt,在他的个人主页(需内网)上可以下载,另外也需要复习pta作业。

(211G0280)C程序设计基础

  • 考核方式:20%作业+30%小测+15%期末上机考+35%期末理论考
Note
  • 22级及以前,非工信类专业若选择c语言课程学习,须和工信类专业学生一起修读该课程。23级开始,新增“C程序设计基础与实验”(211G0310)作为专供工信类专业修读的c语言基础课。因此23级开始,该课程学习内容可能有所变动,请以老师实际安排为准。
  • 但以下经验同样适合修读“C程序设计基础与实验”(211G0310)的同学阅读。
  • 如果你计算机基础没那么好,建议你先使用dev c/在线编程网站/pta自带的编程工具完成该课程的编程要求,不建议为了该课程装clion或visual studio,更不要碰visual studio code。
  • 这门课是基础课中少数需要按照作业正确率给作业分的课程,因此对于不会做的作业建议csdn/gpt查一下,确保pta上是正确的。(这门课抄代码也没啥,自己弄懂即可)
  • 上机课属于历史遗留,现在大家毕竟都有电脑,如果老师不点名不小测的话可以不去。其实正课上不上也差不多,这门课真要学会还是得 多写代码 ,别太指望老师讲会你除了语法以外的问题。
  • 期末考试分为两个部分,理论考和上机考,上机考在后半学期中旬(11月/5月)进行,分为四批,每批题目不一样。需要在半小时完成2道题, 必须完整做对一道题,否则挂科 (如果挂的人太多了会调整,以及22春夏搞了个上机考补考)。理论考按教务网时间进行(不同班时间不一样,大约是分为两批),也会根据情况设置斩杀线。
    • 上机考试由于斩杀线的原因,一向是挂人重灾区。但是课程的目的毕竟是让你学会写代码,因此平时需要除了搞会语法,也要注重调试(debug)技巧的学习,题目是不难的。另外,上机考不考指针(除非你认为scanf也是用了指针)。
    • 理论考就比较抽象了,非常喜欢考一些不是正常人写出来的代码,幸好,我们有前人的总结这个cc98链接这位同学整理了很多笔记,把他们理解,满绩绰绰有余。顺带一提,c语言的权威语法规则详见cppreference,注意我们学习的是c89标准。
  • 我的授课老师是侯启明,他在作业里加了自己出了一道附加题,我认为非常有趣,在这里贴出来,并在最后附上了解题思路。

    犯规七人大战

    裁判用C语言实现了一个简单的RPG游戏,有若干个用 Character 结构体表示的角色:

    struct Character{
    char name[16]; //角色名
    float hp;  //生命值
    float mp;  //魔法值
    };
    
    你需要实现一个函数,代表 name"Player" 的角色,综合运用各种招数,击倒所有其它角色。
    struct Action ActAsPlayer ( struct Character you, const char* enemy_name, struct Action next_action );
    
    每回合,你会收到:

    • 自己的当前状态 you
    • 下一个对手的角色名 enemy_name
    • 对手的预定出招 next_action

    你需要用 Action 结构体返回一个自己的出招,格式和对手的预定出招 next_action 相同:

    struct Action{
        char kind;
        float value;
    };
    
    以下描述仅供参考,实际规则以裁判程序为准。

    其中kind可以是:

    • 'A' 表示攻击,对方的 hp 降低10
    • 'H' 表示治疗,己方的 hp 增加 value ,己方的 mp 消耗 value
    • 'X' 表示爆发,直接将对方 hp 变成0,己方的 mp 消耗999

    ActAsPlayer 返回后,裁判程序会让对战双方依次行动。行动结束后,如果某一方的 hp 小于等于0,那么就判定他/她战斗不能。如果 Player 战斗不能,该测试用例直接判错。否则会换上下一个敌方角色继续战斗。当击败全部敌方角色后,你就可以得到这个测试点的分数。

    如果进行 'H' 或者 'X' 行动时 mp 不足,Player 会被直接判负。

    最初回合 Player 先手,后续回合敌人先手。如果先手角色行动结束时后手角色已经战斗不能,那么后手角色将无法行动。

    仅供参考部分结束。

    包括 Player 在内,最多只会有7个角色参与战斗。

    提示:

    • 实际的裁判程序和题面中完全相同,务必仔细阅读
    • 你的代码和裁判程序是对抗关系而非合作
    • 按题面规则内的手段战斗最多获得一半的分数
    • 这是C程序设计的考题,涉及的知识点是 指针、结构和浮点运算

    裁判测试程序:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <math.h>
    #include <memory.h>
    
    struct Character{
        char name[16];
        float hp;
        float mp;
    };
    
    struct Action{
        char kind;
        float value;
    };
    
    struct Action ActAsPlayer ( struct Character you, const char* enemy_name, struct Action
    next_action );
    
    #define MAX_N_CHARS 8
    
    void PerformAction(
        struct Character* actor,
        struct Character* target,
        struct Action action
    ){
        // Defeated characters can't act
        if(actor->hp<=0.f){return;}
        switch(action.kind){
            case 'A':{
                target->hp-=10.f;
                break;
            }
            case 'H':{
                actor->hp+=action.value;
                actor->mp-=action.value;
                if(actor->mp<0.f){
                    printf("%s healed too much\n",actor->name);
                    actor->hp=0.f;
                }
                break;
            }
            case 'X':{
                if(actor->mp>=999.f){
                    actor->mp-=999.f;
                    target->hp=0.f;
                }
                break;
            }
            default:{
                printf("%s used an invalid action '%c'\n",actor->name,action.kind);
                actor->hp=0.f;
                break;
            }
        }
    }
    
    int main(){
        // Read in the character data
        struct Character characters[MAX_N_CHARS]={};
        struct Action* actions[MAX_N_CHARS]={};
        int p_actions[MAX_N_CHARS]={};
        int n_chars=0;
        int id_player=0;
        int n_read=0;
        n_read+=scanf("%d",&n_chars);
        for(int i=0;i<n_chars;i++){
            int n_actions=0;
            n_read+=scanf("%15s%f%f%d", 
                  characters[i].name, 
                  &characters[i].hp,
                  &characters[i].mp,
                  &n_actions
            );
            // Read in standard action sequences
            actions[i]=(struct Action*)calloc(sizeof(struct Action), n_actions+1);
            for(int j=0;j<n_actions;j++){
                char tmp[2]={};
                n_read+=scanf("%1s%f",tmp,&actions[i][j].value);
                actions[i][j].kind=tmp[0];
            }
            if(strncmp(characters[i].name,"Player",7)==0){
                id_player=i;
            }
        }
        assert(n_read>0);
        // Start the combat
        int player_first=1;
        for(;;){
            // Test for player loss
            if(characters[id_player].hp<=0.f){
                printf("Player has lost\n");
                return 0;
            }
            // Find a surviving enemy
            int id_enemy=-1;
            for(int i=0;i<n_chars;i++){
                if(i!=id_player&&characters[i].hp>0.f){
                    id_enemy=i;
                    break;
                }
            }
            if(id_enemy<0){
                break;
            }
            // Get enemy action
            struct Action act_enemy={};
            do{
                act_enemy=actions[id_enemy][p_actions[id_enemy]++];
                if(!act_enemy.kind){p_actions[id_enemy]=0;}
            }while(!act_enemy.kind);
            // Get player action
            struct Action act_player=ActAsPlayer(
                characters[id_player],
                characters[id_enemy].name,
                act_enemy
            );
            // Act out
            int id_first=id_player;
            int id_second=id_enemy;
            struct Action act_first=act_player;
            struct Action act_second=act_enemy;
            if(!player_first){
                int tmp=id_first;
                id_first=id_second;
                id_second=tmp;
                act_first=act_enemy;
                act_second=act_player;
            }
            PerformAction(
                &characters[id_first],
                &characters[id_second],
                act_first
            );
            PerformAction(
                &characters[id_second],
                &characters[id_first],
                act_second
            );
            player_first=0;
        }
        printf("Player has beaten ");
        const char* sep="";
        for(int i=0;i<n_chars;i++){
            if(i!=id_player){
                printf("%s%s",sep,characters[i].name);
                sep=", ";
            }
        }
        putchar('\n');
    }
    
    /* 请在这里填写答案 */
    
    输入样例
    3
    Goblin 5 3     1 A 0
    Player 80 50   0
    Kobold 15 50   2 H 5 A 0
    
    输出样例
    Player has beaten Goblin, Kobold
    
    样例解释: 两个敌人, Goblin 一直攻击, Kobold 交替治疗/攻击。敌人都很弱,一直 'A' 就能获胜。
    代码长度限制                                                               16 KB
    时间限制                                                                 400 ms
    内存限制                                                                  64 MB
    

解题思路和答案在这里
  • 根据题目暗示“按题面规则内的手段战斗最多获得一半的分数”“涉及的知识点是 指针、结构和浮点运算”以及题目标题“ 犯规 七人大战”,我们需要考虑一些非常规的通过方法。
  • 通过课程学习大家知道指针保存的是地址,我们在需要写的函数中获得了参数 const char* enemy_name ,这个值是保存敌人名字的字符数组 char name[16] 的第0位的地址,而根据结构体 Character的定义, char name[16]float hp的地址是连续的,因此 enemy_name+16 就是变量 hp的起始地址,使用memset函数修改连续四个字节的值为0即可,代码如下。
    struct Action ActAsPlayer (struct Character you, const char* enemy_name, struct Action next_action)
    {
        memset(enemy_name+16,0,4);
        struct Action data;
        data.kind='A';
        data.value=0;
        return data;//随便返回一个不出问题的动作
    }