题目大意:给定一张n个节点m条边的图,定义d数组为每个结点到结点1的距离。
每次可以选择两个操作:1,跳到结点x,dx>d当前 2.跳到结点x,dx<=d当前
注意:操作2最多只能执行1次。求每个点经过操作后到结点1的最小距离
考察知识点:树的广搜,dp思维
题解:首先我们先对树进行广搜,计算出每个点到根节点的距离,然后我们根据距离从大到小进行遍历。对于父亲结点u和儿子节点v,如果d[u]<d[v],f[u]=min(f[u],f[v]) (f数组为最终答案)。否则
f[u]=min(f[u],d[v]),这里是dv的原因是从距离远的跳到距离近的只能跳一次,跳完以后要往大的更新,所以最后f[v]是要大于f[u]的,那么这里选择dv是正确的。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
vector<int>g[maxn],c[maxn];
int vis[maxn], d[maxn],res[maxn];
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
g[i].clear();
c[i].clear();
}
int x, y;
for (int i = 1; i <= m; i++)
{
cin >> x >> y;
g[x].push_back(y);
}
memset(vis, 0, sizeof(vis));
queue<int>q;
q.push(1);
vis[1] = 1;
while (q.size())
{
int u = q.front();
q.pop();
c[d[u]].push_back(u);
int len = g[u].size();
for (int i = 0; i < len; i++)
{
int v = g[u][i];
if (vis[v])continue;
d[v] = d[u] + 1;
vis[v] = 1;
q.push(v);
}
}
/*for (int i = 1; i <= n; i++)cout << d[i] << " ";*/
for (int i = 1; i <= n; i++)res[i] = d[i];
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 = g[u].size();
for (int j = 0; j < len; ++j) {
int v = g[u][j];
if (d[u] < d[v]) res[u] = min(res[u], res[v]);
else res[u] = min(res[u], d[v]);
}
}
}
for (int i = 1; i <= n; ++i) printf("%d ", res[i]);
printf("\n");
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
}