• P5380


    题面

    黑色大模拟一题,身为大模拟爱好者的我当然要A掉它并且写一篇题解。

    刚进来的同学切勿被黑色的难度给吓出去

    切入正题

    题意

    题目中给出了一种很像象棋的棋叫做鸭棋的,具体告诉了你鸭棋的玩法。然后题目告诉你红蓝双方在棋盘上对弈的操作情况,让你模拟棋局,输出一些题目中需要的信息。

    总的来说,一共有七种棋子,他们各有不同的走法,除走法外其他操作几乎与象棋相同。题目中除了移动操作,你还需要关注的是1.是否出现将军局面,2.对局是否结束(即棋盘上有没有王死去)

    我们结合代码片段进行余下的分析和解释。

    代码

    (共8.89KB)

    其实在模拟类题目中不算特别长,因为其中大部分代码都没有大的思维量,并且语法十分简易毫无难度,不必担心。


    我们分析题目中所有需要你做的操作。

    1. 判断移动是否合法
    2. 判断当前是否形成将军局面
    3. 游戏是否结束

    如果我们使用了7个函数来分别解决7种棋子的移动判断,那么首先第1条被解决。接着我们判断落点处是否是对方的王,如果是则对局结束,所以第3条被解决。而我们发现,如果我们在移动后对棋盘上所有的棋子进行判断,判断其能否移动到对方的王上,只要所有棋子中有一颗能够攻击到对方的王,则此时形成将军局面,第2条被解决。

    7个函数

    这里是7种棋子的移动函数,表示我们试图将棋子(与函数种类匹配)从((start_x,start_y))移动到((end_x,end_y)),返回的bool值表示移动能否进行。

    我们观察到题面上用了数学的方法来表示棋子的移动方法,于是我们同样以数学的方法解决棋子的移动。

    初始化部分

    enum chess //棋子种类
    {
        NA,       //无(这个位置是空的)
        captain,  //王
        guard,    //士
        elephant, //象
        horse,    //马
        car,      //车
        duck,     //鸭
        soldier   //兵
    };
    enum belong_type //记录每颗棋子属于何方的类型
    {
        RED,  //属于红方
        BLUE, //属于蓝方
        NOT   //不属于任何一方(即没有棋子)
    };
    
    const int walk_set[2] = {1, -1};               //移动中会使用到的集合
    const string rank_string[2] = {"red", "blue"}; //输出棋子属于哪方时会用到的
    const string chess_string[8] = {"NA", "captain", "guard", "elephant", "horse", "car", "duck", "soldier"};
    //输出棋子名称时会用到的
    
    chess board[10][9];             //棋盘,每一位上保存这一位的棋子种类
    belong_type belong[10][9];      //保存棋盘上每一位上的棋子属于哪方
    bool CHECK_MATE, GAME_OVER;     //将军局面,和游戏是否结束
    int CAPTAIN_X[2], CAPTAIN_Y[2]; //存放双方的王的坐标
    

    王(captain) 的移动

    题面中说明王的可达的位置为((xpm1,y))((x,ypm1))

    第一个for解决((xpm1,y)),第二个for解决((x,ypm1))

    同时如果移动成功,因为棋子是王,所以我们需要记录。

    bool captian_move(int start_x, int start_y, int end_x, int end_y)
    {
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x + walk_set[x], to_y = start_y;
            if (end_x == to_x && end_y == to_y)
            {
                CAPTAIN_X[belong[start_x][start_y]] = end_x;
                CAPTAIN_Y[belong[start_x][start_y]] = end_y;
                return true;
            }
        }
        for (int y = 0; y < 2; y++)
        {
            int to_x = start_x, to_y = start_y + walk_set[y];
            if (end_x == to_x && end_y == to_y)
            {
                CAPTAIN_X[belong[start_x][start_y]] = end_x;
                CAPTAIN_Y[belong[start_x][start_y]] = end_y;
                return true;
            }
        }
        return false;
    }
    

    士(guard) 的移动

    士的可达的位置为((xpm1,ypm1))((xpm1,ymp1))

    第一个for解决((xpm1,ypm1)),第二个for解决((xpm1,ymp1))

    bool guard_move(int start_x, int start_y, int end_x, int end_y)
    {
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x + walk_set[x], to_y = start_y + walk_set[x];
            if (end_x == to_x && end_y == to_y)
                return true;
        }
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x + walk_set[x], to_y = start_y + walk_set[!x];
            if (end_x == to_x && end_y == to_y)
                return true;
        }
        return false;
    }
    

    象(elephant) 的移动

    题面中是这样写的

    可达的位置至多4个,对于任意(s_x,s_y∈{1,-1}),分别有:

    • 如果位置((x+s_x imes1,y+s_y imes1))无任意一方的棋子停留,则((x+s_x imes2,y+s_y imes2))为一个可达的位置。

    按照题面直接翻译成C++(Chinese->C++),我们在walk_set[]中存放的就是({1,-1}),然后直接枚举(s_x)(s_y),直接模拟。

    for的双重循环直接枚举所有的(s_x)(s_y),可以直接套出上面描述中的两个点。

    bool elephant_move(int start_x, int start_y, int end_x, int end_y)
    {
        for (int x = 0; x < 2; x++)
            for (int y = 0; y < 2; y++)
            {
                if (belong[start_x + walk_set[x]][start_y + walk_set[y]] != NOT)
                    continue;
                int to_x = start_x + (walk_set[x] << 1), to_y = start_y + (walk_set[y] << 1);
                if (end_x == to_x && end_y == to_y)
                    return true;
            }
        return false;
    }
    

    马(horse) 的移动

    题面中是这样写的

    可达的位置至多8个,对于任意(s_x,s_y∈{1,-1}),分别有:

    • 如果位置((x+s_x imes1,y))无任意一方的棋子停留,则((x+s_x imes2,y+s_y imes1))为一个可达的位置。
    • 如果位置((x,y+s_y imes1))无任意一方的棋子停留,则((x+s_x imes1,y+s_y imes2))为一个可达的位置。

    (Chinese->C++)

    操作同上,不难理解。

    bool horse_move(int start_x, int start_y, int end_x, int end_y)
    {
        for (int x = 0; x < 2; x++)
            for (int y = 0; y < 2; y++)
            {
                if (belong[start_x + walk_set[x]][start_y] != NOT)
                    continue;
                int to_x = start_x + (walk_set[x] << 1), to_y = start_y + walk_set[y];
                if (end_x == to_x && end_y == to_y)
                    return true;
            }
        for (int x = 0; x < 2; x++)
            for (int y = 0; y < 2; y++)
            {
                if (belong[start_x][start_y + walk_set[y]] != NOT)
                    continue;
                int to_x = start_x + walk_set[x], to_y = start_y + (walk_set[y] << 1);
                if (end_x == to_x && end_y == to_y)
                    return true;
            }
        return false;
    }
    

    车(car) 的移动

    依旧简单,因为车可以在同一直线上不跨越其他棋子进行移动,所以我们对比起点和中点,选取他们相同的一维,在另一维上直接进行for判断直线上有无其他棋子停留。

    bool car_move(int start_x, int start_y, int end_x, int end_y)
    {
        if (start_x == end_x)
        {
            bool check = true;
            for (int y = min(start_y, end_y) + 1; y < max(start_y, end_y); y++)
                check &= belong[start_x][y] == NOT;
            if (check)
                return true;
        }
        if (start_y == end_y)
        {
            bool check = true;
            for (int x = min(start_x, end_x) + 1; x < max(start_x, end_x); x++)
                check &= belong[x][start_y] == NOT;
            if (check)
                return true;
        }
        return false;
    }
    

    鸭(duck) 的移动

    题面中是这样写的

    可达的位置至多8个,对于任意(s_x,s_y∈{1,-1}),分别有:

    • 如果位置((x+s_x imes2,y+s_y imes1))((x+s_x imes1,y))上均无任意一方的棋子停留,则((x+s_x imes3,y+s_y imes2))为一个可达的位置。
    • 如果位置((x+s_x imes1,y+s_y imes2))((x,y+s_y imes1))上均无任意一方的棋子停留,则((x+s_x imes2,y+s_y imes3))为一个可达的位置。

    (Chinese->C++)

    操作与马相同,不难理解。

    bool duck_move(int start_x, int start_y, int end_x, int end_y)
    {
        for (int x = 0; x < 2; x++)
            for (int y = 0; y < 2; y++)
            {
                if (belong[start_x + (walk_set[x] << 1)][start_y + walk_set[y]] != NOT ||
                    belong[start_x + walk_set[x]][start_y] != NOT)
                    continue;
                int to_x = start_x + (walk_set[x] << 1) + walk_set[x], to_y = start_y + (walk_set[y] << 1);
                if (end_x == to_x && end_y == to_y)
                    return true;
            }
        for (int x = 0; x < 2; x++)
            for (int y = 0; y < 2; y++)
            {
                if (belong[start_x + walk_set[x]][start_y + (walk_set[y] << 1)] != NOT ||
                    belong[start_x][start_y + walk_set[y]] != NOT)
                    continue;
                int to_x = start_x + (walk_set[x] << 1), to_y = start_y + (walk_set[y] << 1) + walk_set[y];
                if (end_x == to_x && end_y == to_y)
                    return true;
            }
        return false;
    }
    

    兵(soldier) 的移动

    士的可达的位置为((xpm1,y))((x,ypm1))((xpm1,ypm1))((xpm1,ymp1))

    约等于王+士...

    bool soldier_move(int start_x, int start_y, int end_x, int end_y)
    {
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x + walk_set[x], to_y = start_y;
            if (end_x == to_x && end_y == to_y)
                return true;
        }
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x, to_y = start_y + walk_set[x];
            if (end_x == to_x && end_y == to_y)
                return true;
        }
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x + walk_set[x], to_y = start_y + walk_set[x];
            if (end_x == to_x && end_y == to_y)
                return true;
        }
        for (int x = 0; x < 2; x++)
        {
            int to_x = start_x + walk_set[x], to_y = start_y + walk_set[x ^ 1];
            if (end_x == to_x && end_y == to_y)
                return true;
        }
        return false;
    }
    

    (七个函数共4834B)

    七个函数至此,让我们写一个处理函数来把七个函数凑一起。

    bool choose(int start_x, int start_y, int end_x, int end_y)
    {
        switch (board[start_x][start_y])
        {
        case captain:
            return captian_move(start_x, start_y, end_x, end_y);
            break;
        case guard:
            return guard_move(start_x, start_y, end_x, end_y);
            break;
        case elephant:
            return elephant_move(start_x, start_y, end_x, end_y);
            break;
        case horse:
            return horse_move(start_x, start_y, end_x, end_y);
            break;
        case car:
            return car_move(start_x, start_y, end_x, end_y);
            break;
        case duck:
            return duck_move(start_x, start_y, end_x, end_y);
            break;
        case soldier:
            return soldier_move(start_x, start_y, end_x, end_y);
            break;
        }
        return false;
    }
    

    通俗易懂

    重要的处理

    bool work(int start_x, int start_y, int end_x, int end_y, belong_type turn)
    {
        if (GAME_OVER || belong[start_x][start_y] != turn || belong[end_x][end_y] == turn)
            return false;//游戏结束,或者这颗棋子不是自己的,或者目的地有自己的棋子
        if (!choose(start_x, start_y, end_x, end_y))
            return false;//如果不能移动
        chess moved_chess = board[end_x][end_y];//存放被移走的棋子
        board[end_x][end_y] = board[start_x][start_y];
        belong[end_x][end_y] = belong[start_x][start_y];
        board[start_x][start_y] = NA;
        belong[start_x][start_y] = NOT;
        if (moved_chess == captain)//如果王被移走,游戏结束
            GAME_OVER = true;
        CHECK_MATE = false;//开始准备判定将军局面
        for (int i = 0; i < 10; i++)
            for (int j = 0; j < 9; j++)
                if (belong[i][j] != NOT)//如果有棋子,我们直接判断它是否能攻击到对方王的位置
                    CHECK_MATE |= choose(i, j, CAPTAIN_X[!belong[i][j]], CAPTAIN_Y[!belong[i][j]]);
        printf("%s %s;%s;%s;%s
    ", rank_string[turn].c_str(), chess_string[board[end_x][end_y]].c_str(),
               moved_chess == NA ? "NA" : (rank_string[!turn] + " " + chess_string[moved_chess]).c_str(),
               CHECK_MATE ? "yes" : "no", GAME_OVER ? "yes" : "no");//输出
        return true;
    }
    

    这里便是游戏的主体回合,我们依次判定了移动是否可以进行,然后进行移动,然后判断将军局面和是否结束。

    游戏结束了

    主程序和初始化函数

    void restart(void)
    {
        for (int i = 0; i < 10; i++)
            for (int j = 0; j < 9; j++)
                belong[i][j] = NOT;
        board[0][4] = board[9][4] = captain;
        board[0][3] = board[0][5] = board[9][3] = board[9][5] = guard;
        board[0][2] = board[0][6] = board[9][2] = board[9][6] = elephant;
        board[0][1] = board[0][7] = board[9][1] = board[9][7] = horse;
        board[0][0] = board[0][8] = board[9][0] = board[9][8] = car;
        board[2][0] = board[2][8] = board[7][0] = board[7][8] = duck;
        board[3][0] = board[3][2] = board[3][4] = board[3][6] = board[3][8] =
            board[6][0] = board[6][2] = board[6][4] = board[6][6] = board[6][8] = soldier;
        belong[0][0] = belong[0][1] = belong[0][2] = belong[0][3] = belong[0][4] = belong[0][5] = belong[0][6] = belong[0][7] = belong[0][8] =
            belong[2][0] = belong[2][8] = belong[3][0] = belong[3][2] = belong[3][4] = belong[3][6] = belong[3][8] = RED;
        belong[9][0] = belong[9][1] = belong[9][2] = belong[9][3] = belong[9][4] = belong[9][5] = belong[9][6] = belong[9][7] = belong[9][8] =
            belong[7][0] = belong[7][8] = belong[6][0] = belong[6][2] = belong[6][4] = belong[6][6] = belong[6][8] = BLUE;
        CAPTAIN_X[RED] = 0;
        CAPTAIN_Y[RED] = 4;
        CAPTAIN_X[BLUE] = 9;
        CAPTAIN_Y[BLUE] = 4;
        GAME_OVER = false;
        return;
    }
    
    int main()
    {
        int Q;
        scanf("%d", &Q);
        restart();
        belong_type turn = RED;
        while (Q--)
        {
            int start_x, start_y, end_x, end_y;
            scanf("%d%d%d%d", &start_x, &start_y, &end_x, &end_y);
            if (work(start_x, start_y, end_x, end_y, turn))
                if (turn == RED) //回合变化
                    turn = BLUE;
                else
                    turn = RED;
            else
                puts("Invalid command"); //不合法操作
        }
        return 0;
    }
    

    restart()函数就完全打表了,主程序也十分的通俗易懂

    完整代码

  • 相关阅读:
    ExecutorService 和 NSOperationQueue
    Android 中的AsyncTask
    正则表达式里"-"中划线的使用注意
    Android studio 程序升级和sdk manager 升级方法
    推荐一篇java抽象类和接口区别的文章
    Android measure和layout的一点理解
    Android 屏幕旋转
    Android 中的openurl
    UML 序列图一点理解
    Android Studio中的Module,Facet
  • 原文地址:https://www.cnblogs.com/macesuted/p/solution-P5380.html
Copyright © 2020-2023  润新知