目录

C 属性大爆发

4033

C 属性大爆发

阵地足球的场地可以视为一个 n×m 的二维矩阵,矩阵的元素表示场地的不同部分,其中包含: 墙壁:场地的 上下边界 是墙壁,会阻挡足球的移动; 我方球门:场地的 左侧边界 是我方球门,若足球进入此球门,我方失败; 敌方球门:场地的 右侧边界 是敌方球门,若足球进入此球门,我方获胜; 足球 (O):足球的大小为一个方格,置于场地的某个位置; 障碍 (#):每个障碍占据一个方格,足球无法通过,会阻挡足球的移动; 空地 (.):足球可以在空地自由移动。

玩家可以部署干员来对足球施加力,从而使其移动。 足球的移动方向由干员所在位置决定。为了简化问题,给出干员踢球的方向: U:向上踢球; R:向右踢球; D:向下踢球; L:向左踢球。 若有多个干员同时踢动足球,则足球的移动方向为所有力的合力方向; 足球每秒沿着各分力的方向移动一格,移动 t 秒后会原地停下; 足球撞击墙壁或障碍时会 瞬间反弹,具体如下: 水平 / 垂直移动: 若撞击 墙壁 或 障碍物的侧面,则沿原路径反向弹回。

45 ∘斜向移动: 若撞击 墙壁 或 障碍物的侧面,则按镜像规则反弹; 若撞击 障碍物的角落 或 同时撞击两个障碍物的侧面,则沿原路径反向弹回,不发生镜像反弹。 若足球的运动方向中某个分量同时被正向和反向的墙壁或障碍阻挡,则该分量的作用被抵消,足球的运动方向调整为仅沿未被阻挡的分量方向运动; 若足球根据当前方向的下一个目标格无法抵达,且多次原地反弹后也无法前进,足球将原地静止,不再移动。

若足球进入敌方球门,输出 Win 若足球进入我方球门,输出 Lose 否则,输出足球的最终坐标 (x, y)


朴素想法是分成水平竖直和斜向两种开局状态,且两者之间不会相互转化,水平竖直状态较简单。
关键是处理斜向,最初想法是用一堆映射来描述球可能的所有撞墙后的状态,然后对于每种墙面状态映射一个新的方向,一坨。
当一个位置4个方向都不能走时就静止了,用一个计数变量统计一下即可,不需要用map

更好的做法是用dx,dy两个水平分量来表示当前运动状态。 水平竖直之间dx或者dy为0,自然不用考虑 斜向三面墙会对各自的dx,dy产生影响,判断简洁


{% raw %}

#include<bits/stdc++.h>
using namespace std;

/*
1 5 3
7 O 8
4 6 2
*/

int dir[9][2] = { 
    {0,0},
    {-1,-1},{1,1},{-1,1},{1,-1}, //斜
    {-1,0}, {1,0}, {0,-1},{0,1}, //竖直水平
};

map<int,int> rev = { //反方向
    {1,2},{2,1},{3,4},{4,3},
    {5,6},{6,5},{7,8},{8,7},
};

map<int,array<int,3>> mv = { //斜方向移动判断三个位置
    {1,{1,5,7}},{3,{3,5,8}},{4,{4,7,6}},{2,{2,6,8}},
};

map<pair<int,int>,int> res = { // 斜方向镜面反射
    {{1,5},4},{{1,7},3},
    {{3,5},2},{{3,8},1},
    {{4,7},2},{{4,6},1},
    {{2,6},3},{{2,8},4}, 
};

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m, t, x, y, D = 0;
    cin >> n >> m >> t;

    vector<vector<char>> M(n+2,vector<char>(m+2,'#'));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> M[i][j];
            if (M[i][j] == 'O') {
                x = i, y = j;
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        M[i][0] = M[i][m+1] = '.';
    }

    auto prt = [&] (int a, int b) {
        cout << "("<<a<<", "<<b<<")"<<'\n';
    };

    auto check = [&] (int a,int b) {
        return M[a][b] == '#' ? 1 : 0;
    };

    int player;
    cin >> player;
    int dx = 0, dy = 0;
    for (int i = 1; i <= player; i++) {
        char c;
        cin >> c;
        if (c == 'U') dx--;
        else if (c == 'D') dx++;
        else if (c == 'L') dy--;
        else dy++;
    }
    if (check(x+1,y) && check(x-1,y)) { // 夹住,分量清零
        dx = 0;
    }
    if (check(x,y+1) && check(x,y-1)) {
        dy = 0;
    }
    for (int i = 1; i <= 8; i++) {
        if (dx == dir[i][0] && dy == dir[i][1]) {
            D = i;
        }
    }
    if (D == 0) {
        prt(x,y);
        return 0;
    }
    auto judge = [&] (int a, int b) {
        bool flag = 0;
        if (b == m+1) {
            cout << "Win";
            flag = 1;
        } else if (b == 0) {
            cout << "Lose";
            flag = 1;
        }
        return flag;
    };

    if (D >= 5) { // 竖直水平方向
        while (t--) {
            if (check(x + dir[D][0],y + dir[D][1])) {
                D = rev[D];
            }
            x += dir[D][0], y += dir[D][1];
            if (judge(x,y)) return 0;
        }
        prt(x,y);
        return 0;
    }

    while (t--) { // 斜方向
        auto where = [&] (int dd, int a, int b) {
            bool flag = 1;
            int fx[4], ww[4];
            for (int i = 1; i <= 3; i++) {
                fx[i] = mv[dd][i-1];
                ww[i] =  check(a + dir[fx[i]][0],b + dir[fx[i]][1]);
            }
            if ((ww[1] && !ww[2] && !ww[3]) || (ww[2] && ww[3])) {
                D = rev[D];
            } else if (ww[2] && !ww[3]) {
                D = res[{dd,fx[2]}];
            } else if (ww[3] && !ww[2]) {
                D = res[{dd,fx[3]}];
            } else {
                flag = 0;
            }
            return flag;
        };
        int cnt = 0; // 判断是否困住
        while (where(D,x,y)) { 
            if (++cnt >= 4) {
                prt(x,y);
                return 0;
            }
        }
        x += dir[D][0], y += dir[D][1];
        if (judge(x,y)) return 0;
    }
    prt(x,y);
    return 0;
}

更好的做法,用分量dx,dy来表示改变,只用了50行!

#include<bits/stdc++.h>
using namespace std;
int n,m,t,x,y,k,xp,yp;
char ma[1010][1010];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>t;
    for(int j=0;j<=m+1;j++) ma[0][j]=ma[n+1][j]='#';
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>ma[i][j];
            if(ma[i][j]=='O') x=i,y=j;
        }
    }
    cin>>k;
    char op;
    for(int i=1;i<=k;i++){
        cin>>op;
        if(op=='U') xp--;
        else if(op=='D') xp++;
        else if(op=='L') yp--;
        else yp++;
    }
    if(ma[x][y+yp]=='#'&&ma[x][y-yp]=='#') yp=0;
    if(ma[x+xp][y]=='#'&&ma[x-xp][y]=='#') xp=0;
    while(t){
        t--;
        if(!xp||!yp){
            if(ma[x][y+yp]=='#') yp*=-1;
            if(ma[x+xp][y]=='#') xp*=-1;
        }
        int tot=0;
        while(xp&&yp&&(ma[x+xp][y+yp]=='#'||ma[x][y+yp]=='#'||ma[x+xp][y]=='#')){
            if(ma[x][y+yp]=='#') yp*=-1;
            if(ma[x+xp][y]=='#') xp*=-1;
            if(xp&&yp&&ma[x+xp][y+yp]=='#'&&ma[x][y+yp]!='#'&&ma[x+xp][y]!='#') xp*=-1,yp*=-1;
            tot++;
            if(tot>=5){
                xp=yp=0;
                break;
            }
        }
        x+=xp,y+=yp;
        if(y==0){
            cout<<"Lose";
            return 0;
        }
        if(y==m+1){
            cout<<"Win";
            return 0;
        }
    }
    cout<<"("<<x<<", "<<y<<")";
}

{% endraw %}


推荐文章

flowerpot
最大矩形
上一篇 编译原理笔记
下一篇 操作系统笔记