题解:P7284 [COCI 2020/2021 #4] Patkice II

· · 题解

题目:link

博客园:link

思路

这题怎么敢评绿啊qwq

我们考虑目前的位置是 (i, j),那么如果要去原洋流指向方向不需要改变字符,否则需要。题目要求的是最少改变字符的数量,很明显最短路。

题目转化为:每个位置 (i, j) 连边向上下左右四个方向,如果是原洋流指向方向边权就是 0,否则就是 1,然后求出初始位置到结束位置的最短路长度。最后我们还需要求出改变之后的图,可以发现会改变的只会是最短路径上面的,那么记录 pre_{i, j} 表示 (i, j) 由那个位置转移过来的即可,然后从结束位置反推回去把方向按 pre 数组改了即可。

代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using pii = pair<int, int>;

const int N = 2e3 + 3;
const int INF = 1e9;

struct Node {
  int x, y, d;

  bool operator < (const Node &i) const {
    return d > i.d;
  }
} ;

int n, m, dist[N][N];
pii pre[N][N];
char c[N][N];
priority_queue<Node> pq;

void Push(int x, int y, int t, int k, int xx, int yy) {
  if (!(x > n || x < 1 || y > m || y < 1)) {
    if (dist[x][y] > k + t) {
      dist[x][y] = k + t;
      pre[x][y] = {xx, yy};
      pq.push({x, y, k + t});
    }
  }
}

void Dijkstra() {                     // Dijkstra 或者 BFS 都可以
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      dist[i][j] = INF;
      if (c[i][j] == 'o') {
        pq.push({i, j, -1});
        dist[i][j] = -1;
      }
    }
  }
  while (!pq.empty()) {
    int x = pq.top().x, y = pq.top().y, d = pq.top().d;
    pq.pop();
    if (dist[x][y] != d) continue ;
    if (c[x][y] == 'x') return ;
    int xx = c[x][y] == '^' ? -1 : c[x][y] == 'v' ? 1 : 0, yy = c[x][y] == '<' ? -1 : c[x][y] == '>' ? 1 : 0;
    xx += x, yy += y;
    Push(xx, yy, 0, dist[x][y], x, y), Push(x + 1, y, 1, dist[x][y], x, y), Push(x - 1, y, 1, dist[x][y], x, y), Push(x, y + 1, 1, dist[x][y], x, y), Push(x, y - 1, 1, dist[x][y], x, y);
  }
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      cin >> c[i][j];
    }
  }
  Dijkstra();
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (c[i][j] == 'x') {
        cout << dist[i][j] << "\n";
        for (int k = i, l = j; c[pre[k][l].first][pre[k][l].second] != 'o'; ) {
          int x = pre[k][l].first, y = pre[k][l].second;
          c[x][y] = x - k == 1 ? '^' : x - k == -1 ? 'v' : y - l == 1 ? '<' : '>';
          k = x, l = y;
        }
        for (int k = 1; k <= n; k++) {
          for (int l = 1; l <= m; l++) {
            cout << c[k][l];
          }
          cout << "\n";
        }
      }
    }
  }
  return 0;
}

// 你想知道藏海花的故事吗……