这题值得记录。
题目大意
在有向图 GGG 中,每条边的长度均为 1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
- 路径上的所有点的出边所指向的点都直接或间接与终点连通。
- 在满足条件 1 的情况下使路径最短。
注意:图 GGG 中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
***具体题面请看https://www.luogu.org/problemnew/show/P2296
题解
我的思路是存图的时候另开数组反向存边,然后在反向存边的图中用DFS将从终点出发能走到的点标记为1,然后一遍For循环,枚举标记为1的点的出边所到的点,如果出边所到的点中有标记为0的点,就说明当前点是不符合题目要求1的。
注意:但是我们在这里不能直接删去这个点,如果删去的话,会改变图的形态从而删去更多的点。(我就这里改了一个中午...)
所以我们再开个数组记录哪些点要删去,后统一删去即可,最后跑一遍SPFA。
错误删点方式:
for(int i=1;i<=n;i++) { if(flag[i]) for(int j=head[i];j;j=edge[j].next){ int too=edge[j].to; if(!flag[too]){ flag[i]=0; break; } } }
正确删点方式:
for(int i=1;i<=n;i++) { if(flag[i]&&i!=t) for(int j=head[i];j;j=edge[j].next){ int too=edge[j].to; if(!flag[too]){ judge[i]=1; break; } } } for(int i=1;i<=n;i++){ if(judge[i]) flag[i]=0; }
代码
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #define ll long long int using namespace std; int judge[10008]; int n,m,num,cnt,q[50005],pd[10005],head[10005],HEAD[10005],NUM,s,t,flag[10005],ans[10005]; struct Edge{ int next,dis,to; }edge[200005]; struct EDGE{ int next,dis,to; }road[200005]; void add(int u,int v) { edge[++num].next=head[u]; edge[num].dis=1; edge[num].to=v; head[u]=num; } void ADD(int u,int v){ road[++NUM].next=HEAD[u]; road[NUM].dis=1; road[NUM].to=v; HEAD[u]=NUM; } void dfs(int pos) { for(int i=HEAD[pos];i;i=road[i].next){ int too=road[i].to; if(!flag[too]&&too!=pos){ flag[too]=1; dfs(too); } } } void spfa() { ans[s]=0; q[1]=s; int l=0,r=1; while(l<r) { l++; int now=q[l]; pd[now]=0; for(int i=head[now];i;i=edge[i].next){ int too=edge[i].to; if(!flag[too])continue; if(ans[too]>ans[now]+edge[i].dis){ ans[too]=ans[now]+edge[i].dis; if(!pd[too]){ r++; pd[too]=1; q[r]=too; } } } } } int main() { memset(flag,0,sizeof(flag)); memset(pd,0,sizeof(pd)); memset(ans,0x3f3f3f3f,sizeof(ans)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); ADD(v,u); } scanf("%d%d",&s,&t); flag[t]=1; dfs(t); if(!flag[s]){ printf("-1"); return 0; } for(int i=head[s];i;i=edge[i].next) { if(!flag[edge[i].to]){ printf("-1"); return 0; } } for(int i=1;i<=n;i++) { if(flag[i]&&i!=t) for(int j=head[i];j;j=edge[j].next){ int too=edge[j].to; if(!flag[too]){ judge[i]=1; break; } } } for(int i=1;i<=n;i++){ if(judge[i]) flag[i]=0; } spfa(); if(ans[t]==0x3f3f3f3f) printf("-1"); else printf("%d",ans[t]); return 0; }
我的代码还是比较长的,网上还有一些BFS+DFS的做法代码贼短...