NC23486 小A与小B

题目地址:

https://ac.nowcoder.com/acm/problem/23486

基本思路:

我们看题很容易判断出这题能用解决,我这里有两种的方法,总体来说第二种更优。

  1. 我们初步考虑可不可以进行两次,在第一次过程中记录小A到达每个位置的最短时间,然后在第二次中记录小B到达每个位置的最短时间,最后将小B到达这个位置的时间和小A到达这个位置的时间取最大作为在这个位置的最早相遇时间,最后统计到总体最短相遇时间,这个方法的代码如下,总体复杂度是跑满的。(实际跑了155ms)

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define INF (int)1e18

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int maxn = 1010;
char a[maxn][maxn];
int n,m,dp1[maxn][maxn],dp2[maxn][maxn];
bool vis[maxn][maxn];
int d[8][2] = {{-1, 0}, {0, -1}, {1,  0}, {0, 1}, {-1, 1}, {1, -1}, {-1, -1}, {1, 1}};
signed main() {
  IO;
  queue<pii> que1,que2;
  cin >> n >> m;
  rep(i,1,n){
    rep(j,1,m){
      cin >> a[i][j];
      dp1[i][j] = dp2[i][j] = INF;
      if(a[i][j] == 'C') { que1.push({i,j}); dp1[i][j] = 0;}
      if(a[i][j] == 'D') { que2.push({i,j}); dp2[i][j] = 0;}
    }
  }
  //小A到每个位置的最短时间;
  while (!que1.empty()){
    int x = que1.front().first,y = que1.front().second;
    que1.pop();
    for(int i = 0 ; i < 8 ; i++){
      int nx = x + d[i][0],ny = y + d[i][1];
      if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && dp1[nx][ny] == INF && a[nx][ny] != '#'){
        dp1[nx][ny] = min(dp1[nx][ny],dp1[x][y] + 1);
        que1.push({nx,ny});
      }
    }
  }
  //小B到每个位置的最短时间;
  while (!que2.empty()){
    int x = que2.front().first,y = que2.front().second;
    que2.pop();
    for(int i = 0 ; i < 4 ; i++){
      int nx = x + d[i][0],ny = y + d[i][1];
      if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && dp2[nx][ny] == INF && a[nx][ny] != '#'){
        dp2[nx][ny] = min(dp2[nx][ny],dp2[x][y] + 1);
        que2.push({nx,ny});
      }
    }
  }
  int ans = INF;
  rep(i,1,n){
    rep(j,1,m){
      if(dp1[i][j] == INF || dp2[i][j] == INF) continue;
      //小B每次走两步,所以除二向上取整;
      int res = max(dp1[i][j],(int)ceil((double)dp2[i][j] / 2.0));
      ans = min(ans,res);
    }
  }
  if(ans == INF) cout << "NO" << '\n';
  else{
    cout << "YES" << '\n';
    cout << ans << '\n';
  }
  return 0;
}
  1. 如果说第一种方法类似离线,那么第二种就算是在线的一个,对于每一步我们从小A,和小B两个方向进行双向,对于小A我们每次只走一步也就是一层,而对于小B我们每次两层,在过程中只要出现走过对方走过的路的情况就说明相遇了,这个时候直接结束就能得到答案,总体复杂度是大概率跑不满的。(实际跑了38ms)

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define INF (int)1e18

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int maxn = 1010;
int n,m;
char a[maxn][maxn];
bool vis[2][maxn][maxn];
int d[8][2] = {{-1, 0}, {0, -1}, {1,  0}, {0, 1}, {-1, 1}, {1, -1}, {-1, -1}, {1, 1}};
bool fd = false;
queue<pii> que[2];
void bfs(int f){
  int sz = que[f].size();
  while (sz--){
    int x = que[f].front().first,y = que[f].front().second;
    que[f].pop();
    for(int i = 0 ; i < (f ? 4 : 8) ; i++){
      int nx = x + d[i][0],ny = y + d[i][1];
      if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && !vis[f][nx][ny] && a[nx][ny] != '#'){
        if(vis[f ^ 1][nx][ny]){ fd = true; return; } // 相遇了;
        vis[f][nx][ny] = true;
        que[f].push({nx,ny});
      }
    }
  }
}
int solve(){
  fd = false;
  int cnt = 0;
  while (!que[0].empty() || !que[1].empty()){
    cnt++;
    //小A走一步,小B走两步;
    bfs(0); bfs(1); bfs(1);
    if(fd) return cnt; //这一定是最早的相遇时间;
  }
  return -1;
}
signed main() {
  IO;
  cin >> n >> m;
  mset(vis,false);
  rep(i,1,n){
    rep(j,1,m){
      cin >> a[i][j];
      if(a[i][j] == 'C') { que[0].push({i,j}); vis[0][i][j] = true; }
      if(a[i][j] == 'D') { que[1].push({i,j}); vis[1][i][j] = true; }
    }
  }
  int ans = solve();
  if(ans == -1) cout << "NO" << '\n';
  else {
    cout << "YES" << '\n';
    cout << ans << '\n';
  }
  return 0;
}