E - Escape from the Island
大佬题解,码风真的爱了
状态表式:
f
(
u
,
j
)
f(u,j)
f(u,j)当前在
u
u
u点,已经划了
j
j
j步时离终点的最短距离
状态转移:
主动划一步min,转移到下一个点
v
v
v
f
(
u
,
j
)
=
f
(
v
,
j
+
1
)
+
1
,
(
u
,
v
)
∈
E
,
(
u
,
v
)
∈
E
f(u,j)=f(v,j+1)+1,(u,v)\in E,(u,v)\in E
f(u,j)=f(v,j+1)+1,(u,v)∈E,(u,v)∈E
顺着水流漂max,转移到下一个点
v
v
v
f
(
u
,
j
)
=
f
(
v
,
0
)
+
1
,
(
u
,
v
)
∈
E
f(u,j)=f(v,0)+1,(u,v)\in E
f(u,j)=f(v,0)+1,(u,v)∈E
由于我们知道最终点的状态即
f
(
n
,
j
)
=
0
f(n,j)=0
f(n,j)=0于是考虑bfs倒着进行更新
主动划一步:
(
v
,
j
+
1
)
→
(
u
,
j
)
(v,j+1)\to(u,j)
(v,j+1)→(u,j)
顺水漂一步:
(
v
,
0
)
→
(
u
,
j
)
(v,0)\to(u,j)
(v,0)→(u,j)
非常dt就是顺水飘是最长路更新,而bfs是最短路更新,
比如从
(
u
,
j
)
→
(
v
1
,
0
)
,
(
v
2
,
0
)
…
(
v
k
,
0
)
(u,j)\to(v_1,0),(v_2,0)\dots(v_k,0)
(u,j)→(v1,0),(v2,0)…(vk,0)划了一步,在bfs过程中会始终让离终点最近的点先出队,不妨设出队顺序为
(
v
1
,
0
)
,
(
v
2
,
0
)
…
(
v
k
,
0
)
(v_1,0),(v_2,0)\dots(v_k,0)
(v1,0),(v2,0)…(vk,0)即默认
f
(
v
1
,
0
)
≤
f
(
v
2
,
0
)
≤
⋯
≤
f
(
v
k
,
0
)
f(v_1,0)\leq f(v_2,0)\leq \dots \leq f(v_k,0)
f(v1,0)≤f(v2,0)≤⋯≤f(vk,0),我们只需要让最后出队的点
(
v
k
,
0
)
(v_k,0)
(vk,0)更新
(
u
,
j
)
(u,j)
(u,j)即可,也就是记录一下
u
u
u的出度,当出度为0是就是最后的那个一个点,对其进行更新。
对于无出边的情况,我们让其原地更新
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
typedef pair<int,int> pii;
const int N=100010,M=200010;
int h[N],e[M],ne[M],idx;
int d[N];
bool nd[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int f[N][55];
int n,m,k;
void init()
{
memset(h,-1,sizeof(int)*(n+1));idx=0;
memset(d,0,sizeof(int)*(n+1));
memset(nd,0,sizeof(bool)*(n+1));
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++)
f[i][j]=0x3f3f3f3f;
}
queue<pii> q;
void update(int a,int b,int c,int d)
{
if(f[c][d]==0x3f3f3f3f)
f[c][d]=f[a][b]+1,q.push({c,d});
}
void bfs()
{
for(int i=0;i<=k;i++)
f[n][i]=0,q.push({n,i});
while(q.size())
{
auto [u,t]=q.front();q.pop();
if(t>0)
for(int i=h[u];i!=-1;i=ne[i])
update(u,t,e[i],t-1);
else
{ // t==0
for(int i=h[u];i!=-1;i=ne[i])
{ //0代表顺着水流 1代表逆这水流
if(i%2==0) continue; //倒着更新需要逆着水流
--d[e[i]];
if(!d[e[i]])
for(int j=0;j<=k;j++)
update(u,t,e[i],j);
}
if(nd[u])
for(int j=0;j<=k;j++)
update(u,t,u,j);
}
}
}
int main()
{
IO;
int T=1;
cin>>T;
for(int ca=1;ca<=T;ca++)
{
cin>>n>>m>>k;
init();
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
add(u,v),add(v,u);
++d[u];
}
for(int i=1;i<=n;i++)
if(!d[i]) nd[i]=1;//没有出度
bfs();
printf("Case #%d:\n",ca);
for(int i=1;i<=n;i++)
printf("%d\n",(f[i][0]==0x3f3f3f3f?-1:f[i][0]));
}
return 0;
}
要加油哦~