题目来源:洛谷P2296
思路
一开始看还以为是一道水题 虽然本来就挺水的
本道题的难点在于如何判断是否路径上的点都会直接或者间接连着终点
我们需要在一开始多建一个反向图
然后从终点DFS回去 把路径上的点标记
DFS完之后遍历所有的点 如果当前点没有被标记 说明其不会直接或者间接连着终点
那么我们只需要把没有被标记的点在反向图中到达的点(也就是正向图中到达这个点的前一个点)标记为不计算在图内
PS:这里的标记数组要另外再建一个 如果直接改前面的数组会因为没有更新完就修改而多删除有用的点
这样我们就把所有的不需要遍历的点删除了
最后就是SPFA解决
代码
#include<iostream> #include<queue> using namespace std; #define maxn 100010 queue <int> q; int n,m,cnt2,cnt1,t,w,start,end; int h1[maxn],h2[maxn],dis[maxn]; bool f[maxn],vis[maxn],vis1[maxn];//f为spfa的判断 vis为反向图的判断 vis1删除点的判断 struct Edge { int to; int next; }e1[maxn*20],e2[maxn*20]; void add1(int u,int v) { e1[++cnt1].to=v; e1[cnt1].next=h1[u]; h1[u]=cnt1; } void add2(int u,int v) { e2[++cnt2].to=v; e2[cnt2].next=h2[u]; h2[u]=cnt2; } void dfs(int u) { vis[u]=1; for(int i=h2[u];i;i=e2[i].next) { int v=e2[i].to; if(!vis[v]) dfs(v); } } void spfa()//常规spfa { for(int i=1;i<=n;i++) dis[i]=1e9+7; q.push(start); f[start]=1; dis[start]=0; while(!q.empty()) { int temp=q.front(); q.pop(); f[temp]=0; if(!vis[temp]) continue; for(int i=h1[temp];i;i=e1[i].next) { int v=e1[i].to; if(dis[v]>dis[temp]+1) { dis[v]=dis[temp]+1; if(!f[v]) { f[v]=1; q.push(v); } } } } } int main() { cin>>n>>m; for(int i=1;i<=m;i++) { int x,y; cin>>x>>y; add1(x,y);//正向图 add2(y,x);//反向图 } cin>>start>>end; dfs(end);//从终点DFS for(int i=1;i<=n;i++)//把不用的点删去 { if(!vis[i]) for(int j=h2[i];j;j=e2[j].next) { int v=e2[j].to; vis1[v]=1;//更改新建的数组 } } for(int i=1;i<=n;i++) if(vis1[i]==1) vis[i]=0;//更改不用遍历的点 spfa(); if(dis[end]==1e9+7) cout<<-1; else cout<<dis[end]; }