给定n个点m条边的有向图,
点1为首都,设d(i)为点1到点i的最短路长度。
假设一开始你在点i,你下一步有3种走法:
1.从点i走到点j,同时满足有路且d(i)<d(j)
2.从点i走到点j,同时满足有路且d(i)>=d(j)
3.停止走动
第2种走法最多执行一次,
现在问对于每个点i,走若干次后到达的点s的d(s)最小是多少(即尽量靠近点1)。
数据范围:n,m<=2e5,保证点1可以到达任意城市.
分析:由于终点1是确定的,所以每个点的答案也是确定的,并且更靠近1的点的答案可以转移得出指向它的点的答案。 于是联想到记忆化搜索在回溯的时候转移。
d[i][0]表示点i不用第二种走法的最优解,
d[i][1]表示点i用第二种走法的最优解。
d[1][0]=d[1][1]=dist[1]=0;//边界
d[i][0]=min(dist[i],d[j][0]) (存在i->j ,dist[i]>dist[j])
d[i][1]=min(dist[i] , d[j][1]) (存在i->j ,dist[i]>dist[j])
d[i][1]=min(d[i][1] , d[j][0]) (存在i->j,dist[i]<=dist[j])
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e5+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+5;
int dist[maxn];//最短路
vector<int> g[maxn];
int n,m;
int d[maxn][2];//0表示不用走法2的答案 1表示可以用1次走法2的答案 记忆化搜索回溯的时候转移
bool vis[maxn];
struct node
{
int d,v;
bool operator < (const node &x)const
{
if(d != x.d)
return d>x.d;
return v<x.v;
}
}h;
void dijkstra()
{
priority_queue<node> q;
q.push({0,1});
dist[1]=0;
while(q.size())
{
h=q.top(),q.pop();
if(vis[h.v]) continue;
vis[h.v]=1;
for(int i:g[h.v])
{
if(!vis[i] && dist[i] > h.d+1)
{
dist[i]=h.d+1;
q.push({dist[i],i});
}
}
}
}
int dfs(int v,int f)
{
if(d[v][f] != -1) return d[v][f];
d[v][f]=dist[v];//答案的初值
for(int i:g[v])
{
if(dist[i] > dist[v])//这种不会消耗第二种走法的次数 ,所以f==0也可以往下搜
{
d[v][f]=min(d[v][f],dfs(i,f));
}
else //必须还可以消耗一次第二种走法的次数
{
if(f==1)
{
d[v][f]=min(d[v][f],dfs(i,0));
}
}
}
return d[v][f];
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
int t;cin>>t;
while(t--)
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
g[i].clear();
vis[i]=0;
dist[i]=INF;
d[i][0]=d[i][1]=-1;
}
for(int i=1;i<=m;i++)
{
int v,u;
scanf("%d %d",&v,&u);
g[v].push_back(u);
}
dijkstra();
for(int i=1;i<=n;i++)
{
dfs(i,0),dfs(i,1);
}
for(int i=1;i<=n;i++)
{
printf("%d ",d[i][1]);
}
}
return 0;
}