传送门
本题让求解最坏情况下的最短路,首先这个最坏情况非常难理解,我们假设
d
i
s
[
i
]
[
j
]
dis[i][j]
dis[i][j]表示位于点
i
i
i还剩
j
j
j步的时候在最坏情况下到达终点的最短路。
如果没有这个最坏情况,并且没有 s t e p 2 step2 step2这个转移,那么我们就相当于再无向图的分层图上跑一次 b f s bfs bfs即可。
加入 s t e p 2 step2 step2这个转移后,并且要求最坏情况,那么我们其实只能让 s t e p 2 step2 step2转移达到最坏。
那么当 j = k j=k j=k的时候,我们发现 d i s [ i ] [ j ] dis[i][j] dis[i][j]这个状态一定是由某个状态通过 s t e p 2 step2 step2到达的。而最坏情况下,我们一定要让这个 s t e p 2 step2 step2的转移最不优,也就是说,如果某个未更新状态 d i s [ a ] [ b ] dis[a][b] dis[a][b]可以通过 s t e p 2 step2 step2到达 d i s [ i ] [ k ] dis[i][k] dis[i][k]这个状态,那么一定要在没有其他选项的时候再去转移,也就是说后面再不存在一个点 j j j使得 d i s [ a ] [ b ] dis[a][b] dis[a][b]能够通过 s t e p 2 step2 step2转移到 d i s [ j ] [ k ] dis[j][k] dis[j][k],这时候如果再不转移的话,那么 d i s [ a ] [ b ] dis[a][b] dis[a][b]这个状态就无法到达终点了(这显然不符合规则,即能走到终点不管怎样都一定要走到终点),因此在这个时候转移一定能够保证 d i s [ a ] [ b ] dis[a][b] dis[a][b]在最坏情况下转移。
不过 s t e p 1 step1 step1就必须要保证最优转移,即我们在分层图上从终点 b f s bfs bfs的时候,一旦遇到某个未更新状态可以通过 s t e p 1 step1 step1转移到当前状态,那么就必定要直接更新该状态,因为当前状态是这个未更新状态遇到的第一个状态,后面遇到的状态一定不会更优(根据bfs特点,越后面值越大)。
考虑程序如何实现,首先开两个图,一个有向图(反图,因为要从终点bfs),一个无向图(代码中用g1,g2表示)。然后从先将
d
i
s
[
n
]
[
0
∼
k
]
dis[n][0\sim k]
dis[n][0∼k]加入队列,然后开始bfs,分为两种情况,如果当前状态是
d
i
s
[
i
]
[
k
]
dis[i][k]
dis[i][k],那么只考虑
s
t
e
p
2
step2
step2的转移,为了保证转移最坏,我们记录了反图中每个点的入度,存在
d
e
g
[
]
deg[]
deg[]数组中,当
d
e
g
[
j
]
>
0
deg[j]>0
deg[j]>0的时候,若
j
j
j能通过
s
t
e
p
2
step2
step2到达
i
i
i,首先给
d
e
g
[
j
]
deg[j]
deg[j]减一,注意到
d
e
g
[
j
]
deg[j]
deg[j]若不为0那么意味着
j
j
j点还能通过
s
t
e
p
2
step2
step2转移到其它点,否则的话就直接转移到当前的
d
i
s
[
i
]
[
k
]
dis[i][k]
dis[i][k]状态即可。
如果当前状态时
d
i
s
[
i
]
[
1
∼
k
−
1
]
dis[i][1\sim k-1]
dis[i][1∼k−1],那么就按正常的bfs更新即可,只要遇到一个未更新状态就直接更新(因为要保证最优)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int inf = 2147483647;
const ll INF = 9223372036854775807;
int n,m,k;
int dis[maxn][55],deg[maxn],vis[maxn];
vector<int>g1[maxn],g2[maxn];
struct Node{
int u,o;
};
queue<Node>q;
void bfs(){
for(int i=0;i<=k;++i){
dis[n][i]=0;
q.push(Node{n,i});
}
while(!q.empty()){
int u=q.front().u,o=q.front().o;q.pop();
if(o==k){
for(int i=0;i<g1[u].size();++i){
int v=g1[u][i];
deg[v]--;
if(!deg[v]){
for(int j =0;j<=k;++j){
if(dis[v][j]==-1){
dis[v][j]=dis[u][o]+1;
q.push(Node{v,j});
}
}
}
}
if(vis[u]){
for(int j =0;j<k;++j){
if(dis[u][j]==-1){
dis[u][j]=dis[u][k]+1;
q.push(Node{u,j});
}
}
}
}else{
for(int i =0;i<g2[u].size();++i){
int v=g2[u][i];
if(dis[v][o+1]==-1){
dis[v][o+1]=dis[u][o]+1;
q.push(Node{v,o+1});
}
}
}
}
}
int main(){
int t,kase=0;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i){
g1[i].clear(),g2[i].clear();
deg[i]=0;
vis[i]=0;
}
for(int i=0;i<m;++i){
int u,v;
scanf("%d%d",&u,&v);
deg[u]++;
g1[v].push_back(u);
g2[u].push_back(v);
g2[v].push_back(u);
}
for(int i =1;i<=n;++i){
for(int j=0;j<=k;++j)dis[i][j]=-1;
if(!deg[i])vis[i]=1;
}
bfs();
printf("Case #%d:\n",++kase);
for(int i =1;i<=n;++i){
int ans=inf;
for(int j=0;j<=k;++j)if(dis[i][j]!=-1)ans=min(ans,dis[i][j]);
if(ans==inf)printf("-1\n");else printf("%d\n",ans);
}
}
}