两只牛逃跑到了森林里。Farmer John 开始用他的专家技术追捕这两头牛。你的任务是模拟他们的行为(牛和 John)。
追击在 10 × 10 10 \times 10 10×10 的平面网格内进行。一个格子可以是:一个障碍物,两头牛(它们总在一起),或者 Farmer John。两头牛和 Farmer John 可以在同一个格子内(当他们相遇时),但是他们都不能进入有障碍的格子。
一个格子可以是:
.
空地;*
障碍物;C
两头牛;F
Farmer John。这里有一个地图的例子:
牛在地图里以固定的方式游荡。每分钟,它们可以向前移动或是转弯。如果前方无障碍(地图边沿也是障碍),它们会按照原来的方向前进一步。否则它们会用这一分钟顺时针转 90 度。 同时,它们不会离开地图。
Farmer John 深知牛的移动方法,他也这么移动。
每次(每分钟)Farmer John 和两头牛的移动是同时的。如果他们在移动的时候穿过对方,但是没有在同一格相遇,我们不认为他们相遇了。当他们在某分钟末在某格子相遇,那么追捕结束。
读入十行表示地图。每行都只包含 10 个字符,表示的含义和上面所说的相同。保证地图中只有一个 F
和一个 C
。F
和 C
一开始不会处于同一个格子中。
计算 Farmer John 需要多少分钟来抓住他的牛,假设牛和 Farmer John 一开始的行动方向都是正北(即上)。 如果 John 和牛永远不会相遇,输出 0。
输入共十行,每行 10 个字符,表示如上文描述的地图。
输出一个数字,表示 John 需要多少时间才能抓住牛们。如果 John 无法抓住牛,则输出 0。
*...*.....
......*...
...*...*..
..........
...*.F....
*.....*...
...*......
..C......*
...*.*....
.*.*......
49
翻译来自NOCOW
USACO 2.4
显然这道题有两个核心问题:
如何模拟农民和牛的运动
如何判断农民和牛的相遇
对于第一个问题,笔者倾向于写一个函数来模拟其运动(当然不单独写也可以,但分出来写显得更清晰)。对于农民或牛,用三个数来模拟当前状态:横纵坐标以及当前方向。可以使用一个结构体或者三维数组将其分别打包为农民和牛。关键代码如下:
void move(int t[]) //t是一个三维数组,t[0]表示当前方向,t[1]表示农民或牛横坐标,t[2]表示农民或牛纵坐标
{
if (t[0] == 0) //0代表北,1代表东,2代表南,3代表西
{
if (map[t[1] - 1][t[2]] == '*')//前方是障碍物
{
t[0] = (t[0] + 1) % 4; //转弯
// 或者直接t[0] = 1; 下同
}
else//前方不是障碍物
{
t[1] = t[1] - 1;//直行
}
}
else if (t[0] == 1)
{
if (map[t[1]][t[2] + 1] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[2] = t[2] + 1;
}
}
else if (t[0] == 2)
{
if (map[t[1] + 1][t[2]] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[1] = t[1] + 1;
}
}
else
{
if (map[t[1]][t[2] - 1] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[2] = t[2] - 1;
}
}
}
对于第二个问题,可以枚举出所有状态。由于地图是10*10
大小,在每个格点上有东南西北四种方向可以选择,总共有10*10*4=400
种情况,因此农民和牛总共有400*400=160000
种情况。因此可以开一个数组保存已经经过的状态,如果某个状态第二次出现,即意味着出现了循环,那么农民永远不可能抓到牛。所以问题转化成了:“如何判断当前状态的唯一性”。
容易知道,对于每一个状态,农民和牛的每个特征点都是独一无二的(横纵坐标、当前朝向),因为一旦有重复的,就意味着当前状态已经重复了。为此我们选用一个特征值L=农民横坐标+农民纵坐标*10+牛横坐标*100+牛纵坐标*1000+农民当前朝向*10000+牛当前朝向*40000
。这样,我们就能保证对于每一个状态,其特征值L
都是独一无二的。
完整代码如下:
#include
using namespace std;
int f[3], c[3];//分别表示农民和牛
char map[12][12];//地图大小,简单起见将边界设为'*',于是就是12行
bool flag[200000];//标志数组,判断当前状态是否出现过。稳妥起见要比400*400开的大一些
void move(int t[])
{
if (t[0] == 0)
{
if (map[t[1] - 1][t[2]] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[1] = t[1] - 1;
}
}
else if (t[0] == 1)
{
if (map[t[1]][t[2] + 1] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[2] = t[2] + 1;
}
}
else if (t[0] == 2)
{
if (map[t[1] + 1][t[2]] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[1] = t[1] + 1;
}
}
else
{
if (map[t[1]][t[2] - 1] == '*')
{
t[0] = (t[0] + 1) % 4;
}
else
{
t[2] = t[2] - 1;
}
}
}
bool meet(int f[], int c[])// 判断牛和农民相遇
{
return f[1] == c[1] && f[2] == c[2];
}
int main()
{
int ans = 0;//经历时间
f[0] = 0, c[0] = 0;//初始朝向为北
for (int i = 1; i < 11; i++)
{
for (int j = 1; j < 11; j++)
{
cin >> map[i][j];
//保存农民和牛的初始位置
if (map[i][j] == 'F')
{
f[1] = i;
f[2] = j;
}
if (map[i][j] == 'C')
{
c[1] = i;
c[2] = j;
}
}
}
//将边界也设为障碍物
for (int i = 0; i < 12; i++)
{
map[0][i] = '*';
map[11][i] = '*';
}
for (int j = 0; j < 12; j++)
{
map[j][0] = '*';
map[j][11] = '*';
}
while (!meet(f, c))
{
int p = f[1] + f[2] * 10 + c[1] * 100 + c[2] * 1000 + f[0] * 10000 + c[0] * 40000;//特征值
if (flag[p])
{
cout << 0;
return 0;
}
flag[p] = true;
move(f);
move(c);
ans++;
}
cout << ans;
return 0;
}