• IOI 2009 Mecho


    问题描述:

    小熊Mecho发现了一笔小财富——装满蜂蜜的罐子。它正高高兴兴吃着蜂蜜,突然发现一只蜜蜂看见了自己,并拉响了警报。Mecho知道此刻一群蜜蜂正在从蜂房涌出,并四处涌动试图抓到自己。Mecho知道自己必须赶快离开罐子回到家里,但是蜂蜜实在太好吃了,它不想太早逃离。请你帮助Mecho计算最迟何时离开才能安全地回到家里。

    小熊Mecho所居住的森林可以用正方形网格表示,该正方形网格有nn个单元格组成,它的边分别平行于南北方向和东西方向。每个单元格相邻是指它们在南北方向或者东西方向有一条公共边(即对角线上的单元格不是相邻的)。Mecho是一只笨拙的小熊,每步只能从当前单元格走到相邻的单元格,并且它只能走向草地或者家里,不能通过树木或蜂房,它每分钟最多能走S步。

    警报响起的那一刻,Mecho正位于蜂蜜罐所在的单元格,而蜜蜂则分布于各个蜂房所在的单元格内(森林里可能有不止一个蜂房)。这一刻之后,Mecho按如下规则移动:

    如果Mecho仍在吃蜂蜜,它会决定是继续享用蜂蜜还是开始逃离。如果它决定继续吃蜂蜜,那么在一分钟之内它不会移动。否则,它立即离开并按照上述要求最多走S步。Mecho不能带走任何蜂蜜,所以它一旦离开就不能再吃蜂蜜而只能移动了。

    蜜蜂按照如下规则扩散:

    警报响起时,蜜蜂仅仅占据有蜂房的单元格,第一分钟之后,它们占据所有与蜂房所在的单元格内相邻的草地单元格(包括蜂房所在单元格)。在第二分钟之后,它们多占据一些草地单元格(即与蜂房相邻的草地单元格的相邻草地单元格),依次类推。只要有充足的时间,蜜蜂可以同时占据它们所能到达的所有草地单元格。

    可以理解为在每一分钟Mecho先移动,蜜蜂后扩散,即Mecho在每一分钟内停留或者移动,而蜜蜂在每分钟结束时瞬间扩散。

    注意:Mecho和蜜蜂都不能走到森林之外。另外,根据上述规则,Mecho吃蜂蜜的时间是一个整数。

    在任何时刻,如果Mecho和蜜蜂处于同一单元格,即认为蜜蜂抓住了Mecho。

    任务

    写一个程序,根据给定的森林地图,计算在Mecho安全回到家里而不被蜜蜂抓到的前提下,它可以停留在初始位置吃蜂蜜的最长时间。

    数据规模

    1n800               地图的大小(即正方形的边长)

    1S1,000            Mecho每分钟可以行走的最大步数。

    输入

    你的程序必须从标准输入中读取下列数据:

    第一行包含整数n和S,以一个空格隔开。

    接下来的n行表示森林的地图,其中每行包含n个字符,每个字符代表一个单元格。可能出现的字符和它们的相应意义如下:

    T:表示一棵树木

    G:表示草地单元格

    M:表示Mecho的初始位置,即蜂蜜罐所在的位置,该位置是草地单元格

    D:表示Mecho的家,这一位置Mecho可以进入,而蜜蜂不能进入

    H:表示蜂房的位置

    注意:数据保证地图中只存在一个字符M,一个字符D,至少存在一个字符H。数据保证M可通过一串相邻的G到达M。这串相邻的G的长度可以是0,例如,Mecho家D或者蜂房H与Mecho的起始位置M相邻。另外,蜜蜂不能进入或者跳过Mecho家所在的单元格,对它们来说,Mecho的家就象一棵树一样。


    输出

    你的程序必须向标准输出写入一行,包含一个整数。表示在安全回到家的前提下,Mecho可以停留在初始位置吃蜂蜜的最长时间(单位:分钟)。

    如果Mecho不能安全回到家里,输出-1。

    评分规则

    有40分的评测数据,n60。

     

    样例一

    mecho.in

    mecho.out

    7 3

    TTTTTTT

    TGGGGGT

    TGGGGGT

    MGGGGGD

    TGGGGGT

    TGGGGGT

    THHHHHT

    1

    继续吃蜂蜜1分钟后,Mecho可以用2分钟的时间向右走最短路回到家里,而不被蜜蜂抓到。

    样例二

    mecho.in

    mecho.out

    7 3

    TTTTTTT

    TGGGGGT

    TGGGGGT

    MGGGGGD

    TGGGGGT

    TGGGGGT

    TGHHGGT

    2

    继续吃蜂蜜2分钟后,Mecho在第3分钟走三步,接着在第4分钟走三步,在第5分钟走两步回到家里,而不被蜜蜂抓到。

    分析

    首先,我们有一个第一反应,这个答案一定是可以用二分来得到的,道理很简单:这是一个单调的序列,或者说,这个答案存在这么一个分界点,分界点一下是能够逃脱的,分界点以上就会被蜜蜂抓住。所以我们可以通过二分来找到这个分界点。

    那么我们把问题转移成判断一个时间是否会被蜜蜂抓住。

    首先我们可以画图来理解一下。

    如果 蜜蜂巢-s1-家-s2-蜂蜜 在一条直线上那么吃蜂蜜的时间t = s1/1 - s2/v。这样我们可以推广到不在一条直线上,s1和s2对应了各自点到点的曼哈顿距离。

    这样我们又把问题转换为求各自点到点的曼哈顿距离。这里求距离运用了BFS。

    卡点

    1. 首先读入的时候我们要将起点蜂蜜的地方设置为草地,否则一上来就走不了会很尴尬。但是同时记住把起点位置保存下来。
    2. 这里有两个BFS,一个计算蜜蜂的距离,一个计算小熊的距离,这里密封的距离应该在主程序里,而小熊的距离应该在check函数里。

    程序

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int MAXN = 2000 + 1;
     4 char f[MAXN][MAXN];
     5 int dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1}, B[MAXN][MAXN];
     6 int n, s;
     7 bool flag[MAXN][MAXN];
     8 struct node
     9 {
    10     int x,y;
    11 }Honey, Home, now;
    12 bool check(int delay)
    13 {
    14     if (delay * s >= B[Honey.x][Honey.y])
    15         return false;
    16     memset(flag, 0, sizeof(flag));
    17     deque<pair<int,pair<int,int> > > Q;
    18     Q.push_back(make_pair(delay*s,make_pair(Honey.x,Honey.y)));
    19     flag[Honey.x][Honey.y] = true;
    20     while (!Q.empty())
    21     {
    22         int distance = Q.front().first;
    23         int x = Q.front().second.first;
    24         int y = Q.front().second.second;
    25         Q.pop_front();
    26         if (f[x][y] == 'D')
    27             return true;
    28         for (int c = 0; c < 4; c++)
    29         {
    30             int nx = x + dx[c];
    31             int ny = y + dy[c];
    32             if (nx < 0 || nx >= n || ny < 0 || ny >= n || f[nx][ny] == 'T' || (distance + 1) >= B[nx][ny] || flag[nx][ny])
    33                 continue;
    34             Q.push_back(make_pair(distance + 1, make_pair(nx, ny)));
    35             flag[nx][ny] = true;
    36         }
    37     }
    38     return false;
    39 }
    40 int main()
    41 {
    42     cin >> n >> s;
    43     queue<node> Q;
    44     memset(B,-1,sizeof(B));
    45     for (int i = 0; i < n; i++)
    46     {
    47         cin >> ws;
    48         for (int j = 0; j < n; j++)
    49         {
    50             cin >> f[i][j];
    51             if (f[i][j] == 'H')
    52             { 
    53                 Q.push((node){i,j});
    54                 B[i][j] = 0;
    55             }
    56             if (f[i][j] == 'M')
    57             {
    58                 Honey.x = i, Honey.y = j;
    59                 f[i][j] = 'G';
    60             }
    61             if (f[i][j] == 'D')
    62                 Home.x = i, Home.y = j;
    63         }
    64     }
    65     while (!Q.empty())
    66     {
    67         now = Q.front();
    68         Q.pop();
    69         for (int i = 0; i < 4; i++)
    70         {
    71             node move = (node){now.x + dx[i], now.y + dy[i]};
    72             if (move.x<0 || move.x>=n || move.y<0 || move.y>=n || f[move.x][move.y]!='G' || B[move.x][move.y]!=-1)
    73                 continue;
    74             B[move.x][move.y] = B[now.x][now.y] + s;
    75             Q.push((node){move.x,move.y});
    76         }
    77     }
    78     B[Home.x][Home.y] = n*n*s;
    79     int L = -1, R = 2*n*n;
    80     while (L+1<R)
    81     {
    82         int mid = (L+R) >> 1;
    83         if (check(mid))
    84             L = mid;
    85         else
    86             R = mid;
    87     }
    88     cout << L << endl;
    89     return 0;
    90 }
  • 相关阅读:
    一生不可错过的五句话
    分布式缓存系统Memcached简介与实践[转]
    telnet serverip serverport 可以测试服务器端口是否通
    使用c#生成高品质小空间的缩略图
    sql server2005对tsql的增强之在聚合函数的后面使用over关键字
    (转)让你受益终身的10个Word实用技巧
    sql取所有记录中每天的最后一笔交易的记录
    屏蔽服务器端包含在文件不存在时报错的错误信息
    c#农历日历类
    Niubility 英语教程
  • 原文地址:https://www.cnblogs.com/OIerPrime/p/9097722.html
Copyright © 2020-2023  润新知