描述
在有向图 G 中,每条边的长度均为 1,现给定起点和终点,请你在图中找一条从起点到 终点的路径,该路径满足以下条件:
- 路径上的所有点的出边所指向的点都直接或间接与终点连通。
- 在满足条件 1 的情况下使路径最短。
注意:图 G 中可能存在重边和自环,题目保证终点没有出边。 请你输出符合条件的路径的长度。
格式
输入格式
第一行有两个用一个空格隔开的整数 n 和 m,表示图有 n 个点和 m 条边。
接下来的 m 行每行 2 个整数 x、y,之间用一个空格隔开,表示有一条边从点 x 指向点y。
最后一行有两个用一个空格隔开的整数 s、t,表示起点为 s,终点为 t。
输出格式
输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。
如果这样的路径不存在,输出-1。
限制
对于 30%的数据,0 < n ≤ 10,0 < m ≤ 20;
对于 60%的数据,0 < n ≤ 100,0 < m ≤ 2000;
对于 100%的数据,0 < n ≤ 10,000,0 < m ≤ 200,000,0 < x,y,s,t ≤ n,x ≠ t。
提示
【输入输出样例1说明】
如上图所示,箭头表示有向道路,圆点表示城市。起点 1 与终点 3 不连通,所以满足题目描述的路径不存在,故输出-1。
【输入输出样例2说明】
如上图所示,满足条件的路径为 1->3->4->5。注意点 2 不能在答案路径中,因为点 2 连了一条边到点 6,而点 6 不与终点 5 连通。
解题思路:这个程序感觉是一个宽搜,程序很冒险,因为内存比较大,可以说是恰好过关,但这个程序方法也是
大家普遍能想到的,只不过稍稍改变了一下,就让原本不可能过去的程序过关了。
很巧妙地设计了一个反向的图,两次反着搜,就有结果了。
可能比较绕,因为有的【】里又有好多【】,所以一定要明白每个字母表示什么。
和最优贸易有一点像,如果比成两张地图的话,第一次搜索就是找到能到终点的点,
第二次就是找到最短的路,再将两张地图一重叠,找到最小值,就是答案了。
程序:
program heyingjin;
var m,n,x,y,i,j,s,t,l,r,head,tail:longint;
a:array[1..10000,0..3000]of longint;// 记录到达i,的第j个点,存到a[i,j]中;到i的点的总数存入a【i,0】中
这里存储的比较特别,因为我之前都是i表示起点的,总数存入另一个数组,
它这里既节省了空间,又确保图可以反着来搜;
team,d:array[1..100000]of longint; //team就是建的队,d之后是要记录哪些点是能到终点的,并且到这一个点的步数
v:array[1..10000]of boolean; //也是记录删掉题上说明的不需要的点后的点,记为true
begin
readln(n,m);
fori:=1 to m do
begin
read(x,y);
inc(a[y,0]);a[y,a[y,0]]:=x; //之前已经说过,不解释了。。。
end;
readln(l,r);
fillchar(v,sizeof(v),true); //初始化
team[1]:=r; //反着搜,所以队头设为终点
fori:=1 to n do d[i]:=1000000000;
d[r]:=0; //队头得点赋值0,表示从队头开始往后处理了
head:=0;
tail:=1;
while head<tail do // 维护队伍
begin
inc(head);
for i:=1 to a[team[head],0] do //(以下开始高能烧脑模式,清楚了什么表示什么再来看)a[team[head],0]表示能到队头的所有点的数量;
ifd[team[head]]+1<=d[a[team[head],i]] then //把那些能到队头的点的值变小,之前赋值的100000000
begin
inc(tail);
team[tail]:=a[team[head],i]; //能到队头的点进队
d[a[team[head],i]]:=d[team[head]]+1; //赋值(也就是到这一个点步数)
end;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~以上就找到了能到队头的点,这些点的d【i】值都小于100000000~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fori:=1 to n do
ifd[i]>=1000000000 then // 这一堆表示那些直接连接着到不了终点的点都要消失,也就是false
begin //
v[i]:=false; //
for j:=1 to a[i,0] do //
v[a[i,j]]:=false; //
end; //
v[l]:=true;
team[1]:=r; //再像上面一样,再搜一遍,还是从终点搜到起点,所以队头就还是终点
fori:=1 to n do d[i]:=1000000000; //再次初始化
d[r]:=0;
head:=0;
tail:=1;
while head<tail do //维护队伍
begin
inc(head);
for i:=1 to a[team[head],0] do
if(d[team[head]]+1<=d[a[team[head],i]]) and v[a[team[head],i]] then //表示在裁员以后还能到队头的点
begin
inc(tail);
team[tail]:=a[team[head],i];
d[a[team[head],i]]:=d[team[head]]+1; //d这里就表示到达这个新队尾的步数
end;
end;
ifd[l]=1000000000 then writeln(-1) //这步其实可以写在第一次搜索完后面的,但写在这里感觉条理更清晰(但对于答案为-1的题,更耗时间);
else writeln(d[l]); //输出,结束
end.
对两组数据进行分析一下:
心得:很多时候我都好奇为什么我总做不出程序,原来只是一个细节的差别,我们需要的是一种思维,一种faith。