【问题描述】
有一个 n×m 的网格,其中包含一些实心单元和一些空心单元。网格左上角的坐标为(1, 1),而右下角的坐标为(n, m)。其中有 k 个实心单元,而其他的则是空心的。这时从坐标为( xs,ys )的单元中心向四个对角方向之一(也就是东北、西北、东南和西南)的方向发射一个激光束,如果激光束遇到实心单元或网格边缘则形成反射或折射。方式如下(入射角度为NE为例):
一段时间后,激光束将进入一个死循环,计算在进入死循环之前激光束穿越至少一次的空单元格总数,穿越是指穿过单元中心。
【输入形式】
输入的第一行包含三个整数 n、m 和 k (1≤n、m≤1000, 0≤k≤1000)。接下来的 k 行每行包含两个整数 xi 和 yi (1≤xi≤n,1≤yi≤m),表示第 i 个实心单元的位置。
最后一行包含两个整数xs 、 ys (1≤xs≤n,1≤ys≤m)以及激光发射方向,分别用"NE"、"NW"、"SE"、"SW"代表东北、西北、东南、西南方向。
【输出形式】
输出仅有一行一个数字,表示激光束进入死循环之前所穿越过至少一次的空心单元格的总数。
【样例输入1】
3 3 0 1 2 SW
【样例输出1】
6
【样例输入2】
7 5 3 3 3 4 3 5 3 2 1 SE
【样例输出2】
14
【提示】
可以将 n×m 的网格扩大为(n+2)×(m+2),其中的所有:
(0, i), i=0,1,...,m+1单元
(j, 0),j=0,1, ..., n+1单元
(n+1, i), i=0,1,...,m+1单元
(j, m+1), j=0, 1,...,n+1单元
都可以看做为实心单元。
思路
这道题挺好玩的,思路也不难,就是一个模拟,但是写着比较繁琐。每一个方向上可以分成五种情况,以东北方向为例,如下图:
其余方向上同理。至于判断进入循环,标志就是光线当前所处位置和传播方向与起始时完全一样,这就表明光线即将进入死循环,这时退出循环语句输出结果即可。
记得要用一个二维数组来存储光线经过了哪些空心单元。
代码
#include<iostream>
#include<string>
using namespace std;
int main(){
int n,m,k;
cin>>n>>m>>k;
int** arr=new int*[n+2];
for(int i=0;i<=n+1;++i) arr[i]=new int[m+2];
for(int i=0;i<=n+1;++i){
for(int j=0;j<=m+1;++j){
if(i==0 || j==0 || i==n+1 || j==m+1) arr[i][j]=1;
else arr[i][j]=0;
}
}
int** memo=new int*[n+2];
for(int i=0;i<=n+1;++i) memo[i]=new int[m+2];
for(int i=0;i<=n+1;++i){
for(int j=0;j<=m+1;++j) memo[i][j]=0;
}
for(int i=0;i<k;++i){
int x,y;
cin>>x>>y;
arr[x][y]=1;
}
int xs,ys;
string s;
cin>>xs>>ys>>s;
int m1=xs,m2=ys;
memo[xs][ys]=1;
int d=0,f=0;
if(s=="NE"){d=1; f=1;}
else if(s=="NW"){d=2; f=2;}
else if(s=="SE"){d=3; f=3;}
else{d=4; f=4;}
while(1){
if(d==1){
if(arr[xs-1][ys+1] && arr[xs-1][ys] && arr[xs][ys+1]) d=4;
else if(arr[xs-1][ys+1] && arr[xs-1][ys]){d=3; ys++;}
else if(arr[xs-1][ys+1] && arr[xs][ys+1]){d=2; xs--;}
else if(arr[xs-1][ys+1]) d=4;
else{
xs--; ys++;
}
memo[xs][ys]=1;
}
else if(d==2){
if(arr[xs-1][ys-1] && arr[xs-1][ys] && arr[xs][ys-1]) d=3;
else if(arr[xs-1][ys-1] && arr[xs-1][ys]){d=4; ys--;}
else if(arr[xs][ys-1] && arr[xs-1][ys-1]){d=1; xs--;}
else if(arr[xs-1][ys-1]) d=3;
else{
xs--; ys--;
}
memo[xs][ys]=1;
}
else if(d==3){
if(arr[xs+1][ys+1] && arr[xs][ys+1] && arr[xs+1][ys]) d=2;
else if(arr[xs][ys+1] && arr[xs+1][ys+1]){d=4; xs++;}
else if(arr[xs+1][ys] && arr[xs+1][ys+1]){d=1; ys++;}
else if(arr[xs+1][ys+1]) d=2;
else{
xs++; ys++;
}
memo[xs][ys]=1;
}
else if(d==4){
if(arr[xs+1][ys-1] && arr[xs][ys-1] && arr[xs+1][ys]) d=1;
else if(arr[xs][ys-1] && arr[xs+1][ys-1]){d=3; xs++;}
else if(arr[xs+1][ys] && arr[xs+1][ys-1]){d=2; ys--;}
else if(arr[xs+1][ys-1]) d=1;
else{
xs++; ys--;
}
memo[xs][ys]=1;
}
if(xs==m1 && ys==m2 && d==f) break;
}
int ans=0;
for(int i=0;i<=n+1;++i){
for(int j=0;j<=m+1;++j){
if(memo[i][j]) ans++;
}
}
cout<<ans;
}
写了八十多行。。快比上我写ccf上的大模拟题了。。
一些吐槽
当时写完之后评测以下发现很多都超时了,但给的样例都能过,就很奇怪。调试了半个多小时才发现原来是把有一个加号写成减号导致跳不出永真循环了md。。。这告诉我们写代码一定要细心,不要做一个BugMaker:)