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 %}