这道题我们老师上课讲了,回来琢磨了琢磨,还是不太会,又借鉴了一下博客
话说这道题真坑,首先显然是可以dp的,方程为
这里dp[i]表示第i个点到终点需要的指令数量,
其实这个方程是可以直接 BFS 来算的...
注意到 min(dp(v) + 1) 实际上就是一个边权为 1 的最短路, 也就是 BFS;
而考虑后一项 maxfdp(v)g, 其实是在 BFS 时最后一个更新它的节点去更新它时没有代价.
因为 BFS 的正确性要求队列中的元素距离是不降的, 所以一旦
某个节点被最后一个点更新, 那么我们需要把它加入队首而非队尾.
这个做法是 O(N + M) 的, 因为每个节点我们只需用它更新一次. 一样的道理, 每个点只会入队至多两次
这里我们借助spfa来完成dp
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int dp[2000000], head[1100000], head2[1100000], len, len2;
struct edge{
int v, fail;
edge(){}
edge(int a, int c){
v = a;
fail = c;
}
}e[2200000],e2[2200000];
void add(int a, int b){
e[len] = edge(b, head[a]);
head[a] = len++;
}
void Add(int a, int b){
e2[len2] = edge(a, head2[b]);
head2[b] = len2++;
}
int vis[2200000];
queue<int> q;
int main(){
ios::sync_with_stdio(0);
memset(head, -1, sizeof(head));
memset(head2, -1, sizeof(head2));
int n, m, s, t;
cin >> n >> m;
for(int i = 1; i <= m; i++){
int x, y, z;
cin >> x >> y;
add(x, y);
Add(x, y);//建正图,反图,方便dp
}
cin >> s >> t;
for(int i = 1; i <= n + 20; i++){//初始化
dp[i] = 1147483646;
}
vis[t] = 1;
dp[t] = 0;
q.push(t);
while(!q.empty()){//spfa模板稍加改动
int now = q.front();
q.pop();
vis[now] = 0;
//这里的两个for就分别对应前面转移方程的两种情况
for(int i = head2[now]; ~i; i = e2[i].fail){
if(dp[e2[i].v] > dp[now] + 1){
dp[e2[i].v] = dp[now] + 1;
if(!vis[e2[i].v]){
vis[e2[i].v] = 1;
q.push(e2[i].v);
}
}
}
int Max = 0;
for(int i = head[now]; ~i; i = e[i].fail){
Max = max(Max, dp[e[i].v]);
}
if(dp[now] > Max){
dp[now] = Max;
if(!vis[now]){
vis[now] = 1;
q.push(now);
}
}
}
if(dp[s] != 1147483646)
cout << dp[s] << endl;
else cout << -1 << endl;
return 0;
}