洛谷P1518 [USACO2.4] 两只塔姆沃斯牛 The Tamworth Two

[USACO2.4] 两只塔姆沃斯牛 The Tamworth Two

题目描述

两只牛逃跑到了森林里。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 和一个 CFC 一开始不会处于同一个格子中。

计算 Farmer John 需要多少分钟来抓住他的牛,假设牛和 Farmer John 一开始的行动方向都是正北(即上)。 如果 John 和牛永远不会相遇,输出 0。

输入格式

输入共十行,每行 10 个字符,表示如上文描述的地图。

输出格式

输出一个数字,表示 John 需要多少时间才能抓住牛们。如果 John 无法抓住牛,则输出 0。

样例 #1

样例输入 #1

*...*.....
......*...
...*...*..
..........
...*.F....
*.....*...
...*......
..C......*
...*.*....
.*.*......

样例输出 #1

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

你可能感兴趣的:(算法)