题解 P6705 【[COCI2010-2011#7] POŠTAR】

· · 题解

这个题确实不太简单,因为在二分的时候dfs和check的次数惊人。

这个题如果你做出来了,那么说明你的dfs和二分答案已经炉火纯青了。

所以,如果你是想了一会不会写来看题解,希望你看到这里再回去想想,毕竟这个题对你我觉得会很有启发吧。(等下,先把点赞留下啊喂!)

算法:DFS+二分答案+暴力枚举

我们可以二分从1到当前的的高度。

需要一个方向数组。

还有就是,不要被32MB给吓到,放心去定义吧。

具体看代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define rint register int//稍微快一点
const int maxn = 55;
int n, h[maxn][maxn], ji, s, t, ans = 214748364, now;
bool v1[maxn][maxn], v2[maxn][maxn];
//v1存是否是村庄,v2存是否遍历
int d[10][2] = {{0, 0},{1,0}, {0,1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};

void dfs(int x, int y, int q, int z)
{
    if (x < 1||x > n|| y < 1 || y > n||v2[x][y]||h[x][y] < q||h[x][y] > z)
      return ;//看看能不能走
    v2[x][y] = 1;
    now += v1[x][y];//若有村加1,没有就加0
    for(rint i = 1;i <= 8; ++i)
      dfs(x + d[i][0], y + d[i][1], q, z);
}

bool check(int mid, int q)
{
    memset(v2, 0, sizeof(v2));//每次都要把v2清空

    now = 0;
    dfs(s, t, mid, q);

    return now == ji;//看看所有的村庄是否都被遍历过
}

void cini()
{
    cin >> n;
    for(rint i = 1;i <= n; ++i)
        for(rint j = 1;j <= n; ++j)
        {
            char c;
            cin >> c;
            if(c == 'K')
            {
                ++ji;//村庄数量
                v1[i][j] = 1;
            }
            else if(c == 'P')
              s = i, t = j;//起点
        }

    for(rint i = 1;i <= n; ++i)
      for(rint j = 1;j <= n; ++j)
          cin >> h[i][j];
}

void work()
{
    int l, r, mid;
    for(rint i = 1;i <= n; ++i)
      for(rint j = 1;j <= n; ++j)
      {
        if(h[i][j] < h[s][t])
          continue;//我们只要比起点高的村
        l = 1; r = h[i][j];
        while(l <= r)
        {
            mid = (l + r) >> 1;
            if(check(mid, h[i][j]))
            {
                ans = min(ans, h[i][j] - mid);
                l = mid + 1;
            }else r= mid - 1;
        }
      }
    printf("%d", ans);
}

int main()
{
    cini();
    work();//这样看起来是否会简洁一点呢
    return 0;
}