Codeforces Round #693 (Div. 3) G. Moving to the Capital
题目链接:https://codeforces.com/contest/1472/problem/G
题目大意
n n n个城市, m m m条单向路径,长度都为 1 1 1。 d i d_i di表示从首都 1 1 1到城市 i i i的最短距离。然后就是说从城市 i i i到 j j j有两种行为,第一种随便用,第二种最多只能用一次。我们要求的是从每个城市出发,最终能距首都的最短距离。
解题思路
用 f f f表示最终结果, f [ i ] f[i] f[i]的初值等于 d [ i ] d[i] d[i]。我们按 d d d从大到小枚举城市 u u u, u u u可以到达城市 v v v。
那么如果 d [ u ] < d [ v ] d[u] < d[v] d[u]<d[v],则 u u u 可以直接采用行为 1 1 1到达 v v v,因为是按 d d d从大到小枚举,所以此时 f [ v ] f[v] f[v]是已知的,则可得 f [ u ] = m i n ( f [ u ] , f [ v ] ) f[u] = min(f[u], f[v]) f[u]=min(f[u],f[v])。
如果 d [ u ] > = d [ v ] d[u] >= d[v] d[u]>=d[v],那么 u u u只能采取行为 2 2 2到 v v v,因为已经用过行为 2 2 2,所以 v v v之后只能用 1 1 1了,即只能往 d d d增大的城市走,所以 d [ v ] d[v] d[v] 是最小值,可得 f [ u ] = m i n ( f [ u ] , d [ v ] ) f[u] = min(f[u], d[v]) f[u]=min(f[u],d[v])。
Code
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 200005
int d[MAXN], f[MAXN];
bool vis[MAXN];
vector<int> edges[MAXN], c[MAXN];
int main(void)
{
int T; scanf("%d", &T);
while(T--) {
int n, m; scanf("%d%d", &n, &m);
for(int i=0;i<=n;++i) edges[i].clear(), c[i].clear();
for(int i=0;i<m;++i) {
int u, v; scanf("%d%d", &u, &v);
edges[u].push_back(v);
}
// 求d
memset(vis, false, (n+1) * sizeof(bool));
queue<int> Q;
d[1] = 0; vis[1] = true;
Q.push(1);
while(Q.size()) {
int u = Q.front(); Q.pop();
c[d[u]].push_back(u); // 与1距离相同的点放一起
int len = edges[u].size();
for(int i=0;i<len;++i) {
int v = edges[u][i];
if(vis[v]) continue;
d[v] = d[u] + 1;
vis[v] = true;
Q.push(v);
}
}
// 求结果f
for(int i=1;i<=n;++i) f[i] = d[i]; // f初值等于d
for(int l=n-1;l>0;--l) { // 由远到近遍历每一点
int num = c[l].size();
for(int i=0;i<num;++i) {
int u = c[l][i];
int len = edges[u].size();
for(int j=0;j<len;++j) {
int v = edges[u][j];
if(d[u] < d[v]) f[u] = min(f[u], f[v]);
else f[u] = min(f[u], d[v]);
}
}
}
for(int i=1;i<=n;++i) printf("%d ", f[i]);
printf("\n");
}
return 0;
}