【SPFA+拆点】最短路(path.c/cpp)

 

最短路(path.c/cpp)

[问题描述]

给定一个包含N个点,M条边的无向图,每条边的边权均为1。

再给定K个三元组(A,B,C),表示从A点走到B点后不能往C点走。注意三元组是有序的,如可以从B点走到A点再走到C。

现在你要在K个三元组的限制下,找出1号点到N号点的最短路径,并输出任意一条合法路径,会有Check检查你的输出。

[输入格式]

输入文件第一行有三个数N,M,K,意义如题目所述。

接下来M行每行两个数A,B,表示A,B间有一条边。

再下面K行,每行三个数(A,B,C)描述一个三元组。

[输出格式]

输出文件共两行数,第一行一个数S表示最短路径长度。

第二行S+1个数,表示从1到N所经过的节点。

[样例输入]

4 4 2

1 2

2 3

3 4

1 3

1 2 3

1 3 4

[样例输出]

4

1 3 2 3 4

[数据范围]

对于40%的数据满足N≤10,M≤20,K≤5。

对于100%的数据满足N≤3000,M≤20000,K≤100000。

===========================================

================

type
  pnode=^node;
  node=record
         x:longint;
         next:pnode;
       end;
  tnode=record
          l,x:longint;
        end;
  pnode1=^node1;
  node1=record
          x:longint;
          next:pnode1;
        end;
var
  n,m,k:longint;
  map:array[0..3000]of pnode;
  f_bo:array[0..3000,0..3000]of pnode1;
  dist:array[0..3000,0..3000]of longint;
  dist_l:array[0..3000,0..3000]of longint;
  v:array[0..3000,0..3000]of boolean;
  queue:array[1..100000]of tnode;
procedure init;
begin
  assign(input,'path.in');
  assign(output,'path.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

procedure insert(a,b:longint);
var
  p:pnode;
begin
  new(p);
  p^.x:=b;
  p^.next:=map[a];
  map[a]:=p;
end;

procedure print(a,b:longint);
begin
  if dist_l[a,b]<>0 then print(dist_l[a,b],a);
  write(a,' ');
end;

function can(a,b,c:longint):boolean;
var
  p:pnode1;
begin
  p:=f_bo[a,b];
  can:=true;
  while p<>nil do
    begin
      if p^.x=c then exit(false);
      p:=p^.next;
    end;
end;

procedure spfa;
var
  i:longint;
  l,r:longint;
  x,last:longint;
  p:pnode;
  ans,ansi:longint;
begin
  fillchar(dist,sizeof(dist),$7);
  fillchar(v,sizeof(v),true);
  l:=0; r:=1;
  queue[1].x:=1;
  queue[1].l:=0;
  dist[0,1]:=0; dist_l[0,1]:=0;
  v[0,1]:=false;
  
  repeat
    inc(l);
    x:=queue[l].x;
    last:=queue[l].l;
    p:=map[x];
    while p<>nil do
      begin
        if can(last,x,p^.x)and(dist[last,x]+1<dist[x,p^.x]) then
          begin
            dist[x,p^.x]:=dist[last,x]+1;
            dist_l[x,p^.x]:=last;
            if v[x,p^.x] then
              begin
                v[x,p^.x]:=false;
                inc(r);
                queue[r].l:=x;
                queue[r].x:=p^.x;
              end;
          end;
        p:=p^.next;
      end;
    v[last,x]:=true;
  until l>r;
  ans:=maxlongint;
  for i:=1 to n do
    if dist[i,n]<ans then begin ans:=dist[i,n]; ansi:=i; end;
  writeln(ans);
  print(ansi,n);
  writeln(n);
end;

procedure insert1(a,b,c:longint);
var
  p:pnode1;
begin
  new(p);
  p^.x:=c;
  p^.next:=f_bo[a,b];
  f_bo[a,b]:=p;
end;

procedure main;
var
  i,j:longint;
  a,b,c:longint;
begin
  readln(n,m,k);
  fillchar(f_bo,sizeof(f_bo),0);
  for i:=1 to n do
    for j:=1 to n do f_bo[i,j]:=nil;
  for i:=1 to n do map[i]:=nil;
  for i:=1 to m do begin readln(a,b); insert(a,b); insert(b,a); end;
  for i:=1 to k do begin readln(a,b,c); insert1(a,b,c); end;
  spfa;
end;

begin
  init;
  main;
  terminate;
end.    


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值