原题链接:https://codeforces.ml/contest/1472/problem/G
目录
题意
有n个点,m条单项边,每个点上有三个操作
- 如果dis[u] < dis[v],可以直接从u到v,可以进行无限次操作
- 如果dis[u] >= dis[v],可以从u到v,只能操作一次
- 待在u节点不动
问从i节点出发能到距离1节点最近的距离是多少?
分析
如果直接建图,然后每个点上遍历一次肯定可以求出答案,但时间会超时,因此考虑记录已经遍历到的节点。
先预处理一遍最短路,用bfs就可以实现
接着设一个状态f[x][2]表示从x节点出发可以到距离1最近的位置是多少,后面0代表没用过2操作,1代表已经用了2操作。
然后就是状态转移的过程
- 操作1:f[x][flag] = min(f[x][flag], dfs(v, flag))
- 操作2:f[x][flag] = min(f[x][flag], dfs(v, 1))
- 操作3:f[x][flag] = min(f[x][flag], dis[x])
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 100, M = 5e5 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int f[N][2], dis[N], vis[N];
vector<int> g[N];
void bfs(int s) {
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
queue<int> q; q.push(s); dis[s] = 0;
while (q.size()) {
int u = q.front();
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto v : g[u]) {
dis[v] = min(dis[v], dis[u] + 1);
q.push(v);
}
}
}
int dfs(int x, int flag) {
if (f[x][flag] != INF) return f[x][flag];
f[x][flag] = dis[x];
for (auto v : g[x]) {
if (dis[v] > dis[x]) {
f[x][flag] = min(f[x][flag], dfs(v, flag));
} else {
if (!flag) {
f[x][flag] = min(f[x][flag], dfs(v, 1));
}
}
}
return f[x][flag];
}
void solve() {
int T; cin >> T; while (T--) {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
}
bfs(1);
memset(f, 0x3f, sizeof f);
for (int i = 1; i <= n; i++) {
cout << dfs(i, 0) << " ";
}
cout << endl;
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}