zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    Note/Solution 转置原理 & 多点求值
    Note/Solution 「洛谷 P5158」「模板」多项式快速插值
    Solution 「CTS 2019」「洛谷 P5404」氪金手游
    Solution 「CEOI 2017」「洛谷 P4654」Mousetrap
    Solution Set Border Theory
    Solution Set Stirling 数相关杂题
    Solution 「CEOI 2006」「洛谷 P5974」ANTENNA
    Solution 「ZJOI 2013」「洛谷 P3337」防守战线
    Solution 「CF 923E」Perpetual Subtraction
    KVM虚拟化
  • 原文地址:https://www.cnblogs.com/OIerPrime/p/9097722.html
Copyright © 2011-2022 走看看