E-小A与小B_【2023陕西暑假集训】一场快乐的比赛 (nowcoder.com)
双向广搜,一个简单bfs,一个01bfs。
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <ctime>
#include <random>
#include <sstream>
#include <numeric>
#include <stdio.h>
#include <functional>
#include <bitset>
#include <algorithm>
using namespace std;
// #define Multiple_groups_of_examples
#define IOS std::cout.tie(0);std::cin.tie(0)->sync_with_stdio(false);
#define dbgnb(a) std::cout << #a << " = " << a << '\n';
#define dbgtt cout<<" !!!test!!! "<<endl;
#define rep(i,x,n) for(int i = x; i <= n; i++)
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define vf first
#define vs second
typedef long long LL;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 21;
void inpfile();
void solve() {
int n,m; cin>>n>>m;
PII Asit,Bsit;
vector<vector<char>> ph(n, vector<char>(m));
vector<vector<int>> vis(n, vector<int>(m));
rep(i,0,n-1) {
rep(j,0,m-1) {
cin>>ph[i][j];
if(ph[i][j] == 'C') Asit = {i,j};
if(ph[i][j] == 'D') Bsit = {i,j};
}
}
// dbgtt
vector<int> fAx({-1, -1, -1, 1, 1, 1, 0, 0}), fAy({-1, 0, 1, -1, 0, 1, -1, 1}); // A 走的方向
vector<int> fBx({1,-1, 0, 0}), fBy({0,0,1,-1}); // B 走的方向
deque<array<int,4>> who; // x, y cnt, 1是A, 3是B
vis[Asit.vf][Asit.vs] = 1;
vis[Bsit.vf][Bsit.vs] = 1;
who.push_front({Bsit.vf, Bsit.vs, 0,3});
who.push_front({Asit.vf, Asit.vs, 0, 1});
vis[Asit.vf][Asit.vs] = 1;
vis[Bsit.vf][Bsit.vs] = 2;
auto check = [&](int x, int y) -> bool { // check是否位置不合法
if(x < 0 || x >= n || y < 0 || y >= m) return true;
return false;
};
map<PII,int> mpii; // 记录cnt
/**
* 队列里的cnt可以用mpii代替
*/
mpii[Asit] = 0;
mpii[Bsit] = 0;
int mi = INF;
int ans = INF;
while(who.size()) {
auto tmp = who.front(); who.pop_front();
int x = tmp[0], y = tmp[1], cnt = tmp[2], now = tmp[3];
if(cnt > ans + 3) continue; // 做个剪枝,当已经有答案了,且当前cnt大于ans比较多,continue
/**
* 这里不能直接搜到结果就break。
* 因为这两个人走的每个位置都是一个比较大的范围,可能先得到的答案不对。
* 但是跑完全图可能超时,因此用个剪枝
*/
if(now == 3 || now == 2) { // 是B
rep(i,0,3) {
int xx = x + fBx[i], yy = y + fBy[i];
if(check(xx,yy)) continue;
if(ph[xx][yy] == '#') continue;
if(ph[xx][yy] == 'C') {
int luj = mpii[{xx,yy}];
int buj = cnt;
if(now == 3) buj++;
mi = max(luj, buj);
ans = min(mi, ans);
break;
}
if(vis[xx][yy] == 2 || vis[xx][yy] == 3) continue;
if(vis[xx][yy] == 0) {
/**
* 这一步的B走到这,该位置没有被访问过。
* 有两种情况:
* 一,走了一次
* 还有一次的免费机会
* 二,走了两次
* 没有免费的机会了,又是一次轮回(
* 如果now == 3,表示还有新的两段开始,时间要加一
* 如果now == 2,表示之前用了记时间,还有一次的免费步数。
* 因此,采用 01bfs
*/
vis[xx][yy] = 2;
int buj = cnt;
if(now == 3) buj++;
int nn = (now == 3 ? 2 : 3);
mpii[{xx,yy}] = buj;
if(now == 3) { //
who.push_front({xx,yy,buj,nn}); // now == 3, 01bfs = 0
} else who.push_back({xx,yy,buj,nn}); // now == 2, 01bfs = 1
} else {
if(vis[xx][yy] == 1) {
/**
* 这里记录时间,
* 两个时间取最大的,
* 对ans求最小的。
* (之前看成了求距离了!
*/
int luj = mpii[{xx,yy}];
int buj = cnt;
if(now == 3) buj++;
mi = max(luj, buj);
ans = min(mi, ans);
break;
} else {
continue;
}
}
}
} else { // A
rep(i,0,7) {
/**
* 同B,简单bfs
*/
int xx = x +fAx[i], yy = y + fAy[i];
if(check(xx,yy)) continue;
if(ph[xx][yy] == '#') continue;
if(ph[xx][yy] == 'D') {
int luj = mpii[{xx,yy}];
int buj = cnt + 1;
mi = max(luj, buj);
ans = min(mi, ans);
break;
}
if(vis[xx][yy] == 1) continue;
if(vis[xx][yy] == 0) {
vis[xx][yy] = 1;
int buj = cnt + 1;
mpii[{xx,yy}] = buj;
who.push_back({xx,yy,buj,1});
} else {
if(vis[xx][yy] != 1) {
int luj = mpii[{xx,yy}];
int buj = cnt + 1;
mi = max(luj, buj);
ans = min(mi, ans);
break;
} else {
continue;
}
}
}
}
}
if(ans == INF) puts("NO");
else {
puts("YES");
cout<<ans;
}
}
int main()
{
#ifdef Multiple_groups_of_examples
int T; cin>>T;
while(T--)
#endif
solve();
return 0;
}
void inpfile() {
#define mytest
#ifdef mytest
freopen("ANSWER.txt", "w",stdout);
#endif
}