【问题描述】
有一个 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单元
都可以看做为实心单元。
【评分标准】274E
【思路分析】
模拟小球运动,将小球经过的区域赋值为2,实心块赋值为1,小球到达数组值为1的位置时,进行碰撞的一系列模拟,最后统计数组中为2的个数。
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int n,m,k,cnt=0,cishu=1000000;
cin>>n>>m>>k;
int num[n+2][m+2];
for(int i=0;i<n+2;i++)
memset(num[i],0,4*(m+2));
for(int i=0;i<k;i++){
int x,y;
cin>>x>>y;
num[x][y]=1;
}
for(int i=0;i<=n+1;i++)
num[i][0]=1,num[i][m+1]=1;
for(int i=0;i<=m+1;i++)
num[0][i]=1,num[n+1][i]=1; //初始化数组,实心块赋值为1
int x_l,y_l;
char fx1,fx2;
int v_x=1,v_y=1; //设置激光移动速度,每次移动一格
cin>>x_l>>y_l>>fx1>>fx2;
num[x_l][y_l]=2;
if(fx1=='N')
v_x*=(-1);
if(fx2=='W')
v_y*=(-1); //设置初速度方向
while(cishu--)
{
if(num[x_l][y_l]==1) //碰到实心块进行碰撞模拟
{
if(num[x_l-v_x][y_l]==1&&num[x_l][y_l-v_y]==1)
v_x*=(-1),v_y*=(-1);
else if(num[x_l-v_x][y_l]==1)
{
v_y*=(-1);
y_l+=v_y;
num[x_l][y_l]=2;
}
else if(num[x_l][y_l-v_y]==1)
{
v_x*=(-1);
x_l+=v_x;
num[x_l][y_l]=2;
}
else
v_x*=(-1),v_y*=(-1); //几种碰撞方式
}
x_l+=v_x,y_l+=v_y;
if(num[x_l][y_l]!=1)
num[x_l][y_l]=2; //激光移动,并将所到处赋值为2
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(num[i][j]==2)
cnt++; //统计数组中为2个数
cout<<cnt;
return 0;
}