AT_abc413_f 题解
题目传送门
AtCoder413 F - No Passage
vjudge AtCoder abc413_f - No Passage ( +中文翻译 )
题面
给定 \(H*W\) 的网格棋盘,上面有 \(K\) 个终点, 最开始棋盘有一个棋子,高桥和青木会重复以下操作,如果棋子到达了任意一个终点就停下来。
- 首先青木从上下左右中选择一个方向,并禁用这个方向(禁用仅限于高桥本次的操作)
- 然后高桥从除了青木选的方向以外的三个方向中选择一个方向移动棋子,如果会导致出界则当作棋子不移动。
当然,如果棋子最开始就在终点,那么这个棋子不需要移动。
青木的目标是使高桥无法让棋子到达任何一个终点。如果做不到也要让高桥的操作次数尽可能多。
高桥的目标是在尽可能少的操作次数中将棋子移到任意一个终点。
我们假设高桥和青木聪明绝顶,一定会以最优的方式去操作。
我们令\(f(i,j)\)为如果棋子从\((i,j)\)出发,最终到达某一个终点需要的操作次数,注意:如果青木可以使高桥无法让棋子到达任何一个终点,那么\(f(i,j) = 0\)
你要求的是:
\[\sum_{i=1}^H \sum_{j=1}^W f(i,j)
\]
思路
我们令\(dis_{i,j}\)存储\(f(i,j)\)
首先很显然:如果\((i,j)\)位于一个终点上,那么\(dis_{i,j} = 0\)
然后如果没有青木的干扰,让高桥随便走,那么每次只要走周围的dis最小的位置即可,那么很好解决:BFS即可
可是烦人的青木来了的话,他每次一定会把最小的dis的方向堵掉,那么高桥就只能走次小了,所以BFS即可,但每一次求某一个dis时都是四面的位置的次小值。
代码
#include <bits/stdc++.h>
using namespace std;
int dis[3002][3002];
int dr[4] = { 1, -1, 0, 0 };
int dc[4] = { 0, 0, 1, -1 };
int calculate(int x, int y) // 找x,y四面的dis的次小值
{
int a[4] = { dis[x - 1][y], dis[x + 1][y],
dis[x][y - 1], dis[x][y + 1] };
sort(a, a + 4);
return a[1];
}
int main()
{
memset(dis, 0x3f, sizeof(dis)); // 初始化别忘了
queue<pair<int, int> > q;
int h, w, k;
cin >> h >> w >> k;
for (int i = 1; i <= k; i++)
{
int r, c;
cin >> r >> c;
q.push( { r, c } );
dis[r][c] = 0; // 终点的dis为0
}
while (!q.empty()) // BFS求dis
{
int r = q.front().first;
int c = q.front().second;
q.pop();
for (int i = 0; i < 4; i++)
{
int nr = r + dr[i], nc = c + dc[i];
if (nr < 1 || nc < 1 || nr > h || nc > w) // 判断一下出界
{
continue;
}
// 注意每次我们更新的是dis[nr][nc]而非dis[r][c],不然初始时设的0会被无穷大无情的覆盖掉
int t = calculate(nr, nc) + 1; // dis[nr][nc]周围的次小值还要+1, 因为还要从(nr,nc)走过去嘛
if (t < dis[nr][nc]) // 看看能否更新dis[nr][nc]
{
dis[nr][nc] = t;
q.push( { nr, nc } ); // 把q放在里面
}
}
}
long long s = 0;
for (int i = 1; i <= h; i++)
{
for (int j = 1; j <= w; j++)
{
s += (dis[i][j] == 0x3f3f3f3f ? 0 : dis[i][j]); // 注意0x3f3f3f3f要改成0哦!
}
}
cout << s << endl;
return 0;
}

浙公网安备 33010602011771号