spfa专题

SPFA专题

1通往奥格瑞玛的道路

在艾泽拉斯,有n个城市。编号为1,2,3,...,n

城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

假设1为暴风城,n为奥格瑞玛,而他的血量最多为b,出发时他的血量是满的。

歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

输入输出格式

输入格式:

第一行3个正整数,nmb。分别表示有n个城市,m条公路,歪嘴哦的血量为b

接下来有n行,每行1个正整数,fi。表示经过城市i,需要交费fi元。

再接下来有m行,每行3个正整数,aibici(1<=aibi<=n)。表示城市ai和城市bi之间有一条公路,如果从城市ai到城市bi,或者从城市bi到城市ai,会损失ci的血量。

输出格式:

仅一个整数,表示歪嘴哦交费最多的一次的最小值。

如果他无法到达奥格瑞玛,输出AFK

输入输出样例

输入样例#1 复制

4 4 8

8

5

6

10

2 1 2

2 4 1

1 3 4

3 4 3

输出样例#1 复制

10

说明

对于60%的数据,满足n≤200m≤10000b≤200

对于100%的数据,满足n≤10000m≤50000b≤1000000000

对于100%的数据,满足ci≤1000000000fi≤1000000000,可能有两条边连接着相同的城市。

 

最大值最小?二分答案。但有血量怎么办?因为你想要走到奥格瑞玛,你不能死,那血扣的最少的时候你还死了说明不可能通过,也就是AFK。所以这可以刚开始用一个很大的答案试试,如果能通过,说明可行。然后二分答案,这里要注意一个大大大坑,你快排了cost,它所对应的节点也就改变了,就是说通过i节点的费用不是原来的了。所以你需要一个临时数组来代替cost快排,然后将这个数组作为二分答案的数组。他们两两不干扰。要注意数组后效性

  1 var
  2 x,y,z,n,m,b,mi,sum,l,r,mid,blood,ma,ans:int64;
  3 a,w,cost,next,head,q,dis,c:array[0..200005]of int64;
  4 vis,bj:array[0..200005]of boolean;
  5 i,e:longint;
  6   procedure sort(l,r:int64);
  7       var
  8          i,j,x,y:int64;
  9       begin
 10          i:=l;
 11          j:=r;
 12          x:=c[(l+r) div 2];
 13          repeat
 14            while c[i]<x do
 15             inc(i);
 16            while x<c[j] do
 17             dec(j);
 18            if not(i>j) then
 19              begin
 20                 y:=c[i];
 21                 c[i]:=c[j];
 22                 c[j]:=y;
 23                 inc(i);dec(j);
 24              end;
 25          until i>j;
 26          if l<j then   sort(l,j);
 27          if i<r then   sort(i,r);
 28       end;
 29 
 30 procedure add(x,y,z:longint);
 31  begin
 32   inc(e);a[e]:=y;next[e]:=head[x];head[x]:=e;w[e]:=z;
 33  end;
 34  function check(mid:longint):boolean;
 35   var h,t,u,v,i:longint;
 36    begin
 37      h:=0;t:=1;
 38        fillchar(q,sizeof(q),0);
 39       fillchar(vis,sizeof(vis),false);
 40        fillchar(bj,sizeof(bj),false);
 41      q[1]:=1; vis[1]:=true;
 42     for i:=1 to n do dis[i]:=1<<30;
 43     for i:=1 to n do if mid<cost[i] then bj[i]:=true;
 44     dis[1]:=0;
 45       while h<t do
 46         begin
 47          inc(h);
 48          u:=q[h];
 49          i:=head[u];
 50          vis[u]:=false;
 51          while i>0 do
 52            begin
 53              v:=a[i];
 54           //   if mid=5 then writeln(u,' ',v);
 55               if (dis[u]+w[i]<dis[v])and(not bj[v]) then
 56                begin
 57                  dis[v]:=dis[u]+w[i];
 58                    if (not vis[v])and(not bj[v]) then
 59                       begin
 60                         vis[v]:=true;
 61                          inc(t);
 62                         q[t]:=v;
 63                       end;
 64                 end;
 65                i:=next[i];
 66            end;
 67         end;
 68        if dis[n]>b then exit(false) else exit(true);
 69   end;
 70 begin
 71  readln(n,m,b);
 72  mi:=maxlongint;
 73  for i:=1 to n do
 74   begin
 75     readln(cost[i]);
 76     if ma<cost[i] then ma:=cost[i];
 77     if mi>cost[i] then  mi:=cost[i];
 78 
 79   end;
 80   c:=cost;
 81   sort(1,n);
 82   for i:=1 to m do
 83    begin
 84     readln(x,y,z);
 85     add(x,y,z);
 86     add(y,x,z);
 87    end;
 88    l:=1; r:=n;
 89    if not check(maxlongint) then
 90   begin   writeln('AFK'); exit; end;
 91 
 92   while l<=r do
 93     begin
 94       mid:=(l+r)div 2;
 95      // blood:=b;
 96 
 97      if check(c[mid]) then
 98      begin ans:=c[mid]; r:=mid-1; end else l:=mid+1;
 99     end;
100 
101     writeln(ans);
102 end.

最优贸易noip2009

 

题解:

 

就因为我for 打错成n就调了一个小时!?而且这已经不是第一次了!

 

------------------------------------------------------------------------------------

 

开始说正事。

 

本题很直观的就是想找到一个价格最低和一个价格最高点(满足由起点能到达且又可顺序到达终点),找最低价格的点可以这样来处理, 从源点开始找到所有点的最低价格(某个顶点的最低价格就是从源点到该所有可能路径上的价格最低的点), 最后枚举卖出的点,并且判断该点是否可以到达即可。 此外,由于数据规模较大,一般的邻接矩阵难以承受, 因此采用动态数据结构邻接表。但是本题有环,处理时还有几个细节问题: 1.由于是最后要判定所有的顶点是否可以到达终点, 因此不妨将所有的边反向,由终点出发判断终点可以到达那些顶点就可以了, 并做上标记。这样也就要求在读入边的时候必须反向再储存一次。 2.用SPFA求某个顶点的最低价格时,对SPFA进行了改进。 由SPFA的原理可以知道,该算法可以处理环的问题, 但是要求最短路径的长度是可以收敛的,也就是说不能在图中存在负权。 该题目满足此要求。SPFA是用来求最短路径的算法,在此对其改进, 来求路径上最小价格的点。

 

  1 var
  2 e,ie,x,y,z,u,h,t,n,m,i,j,k,ans,v:longint;
  3 head,b,next,ihead,inext,a,ib,maxp,minp,q:array[0..500005]of longint;
  4 vis:array[0..500005]of boolean;
  5 function max(x,y:longint):longint;
  6  begin
  7   if x>y then exit(x) else exit(y);
  8  end;
  9  function min(x,y:longint):longint;
 10   begin
 11    if x>y then exit(y) else exit(x);
 12   end;
 13 procedure add(x,y:longint);
 14  begin
 15   inc(e);b[e]:=y;next[e]:=head[x];head[x]:=e;
 16  end;
 17   procedure iadd(x,y:longint);//反向建边
 18  begin
 19   inc(ie);ib[ie]:=y;inext[ie]:=ihead[x];ihead[x]:=ie;
 20  end;
 21  procedure spfa1;
 22   var i,j,h,t,u,v:longint;
 23   begin
 24    for i:=1 to n do minp[i]:=maxlongint;
 25    h:=0;t:=1; q[1]:=1;vis[1]:=true;
 26    minp[1]:=a[1];
 27    while h<t do
 28     begin
 29      inc(h);
 30      u:=q[h];
 31     vis[u]:=false;
 32      i:=head[u];
 33      while i>0 do
 34       begin
 35        v:=b[i];
 36        if minp[v]>minp[u] then
 37         begin
 38              minp[v]:=minp[u];
 39          minp[v]:=min(minp[v],a[v]);
 40          if not vis[v] then
 41           begin
 42             vis[v]:=true;
 43             inc(t);
 44             q[t]:=v;
 45           end;
 46           end;
 47            i:=next[i];
 48            end;
 49     end;
 50     
 51   end;
 52  procedure spfa2;
 53  var i,j,h,t,u,v:longint;
 54   begin
 55   fillchar(q,sizeof(q),0);
 56   fillchar(vis,sizeof(vis),false);
 57    h:=0;t:=1;
 58    q[1]:=n; vis[n]:=true;
 59    maxp[n]:=a[n];
 60    while h<t do
 61      begin
 62        inc(h);
 63        u:=q[h];
 64        i:=ihead[u];
 65       vis[u]:=false;//打在前面有负环也能运行
 66         while i>0 do
 67           begin
 68             v:=ib[i];
 69           if maxp[v]<maxp[u] then // you wrong here
 70             begin
 71               maxp[v]:=maxp[u];
 72              maxp[v]:=max(maxp[v],a[v]);
 73             if not vis[v] then
 74              begin
 75                vis[v]:=true;
 76                inc(t);
 77                q[t]:=v;
 78              end;
 79             end;
 80             i:=inext[i];
 81            
 82           end;
 83      end;
 84 
 85   end;
 86 begin
 87  readln(n,m);
 88  for i:=1 to n do
 89  read(a[i]);
 90  for i:=1 to m do//m 不要再打成n了!
 91   begin
 92    readln(x,y,z);
 93    add(x,y);
 94    iadd(y,x);
 95     if z=2 then
 96      begin
 97        add(y,x);
 98        iadd(x,y);
 99      end;
100   end;
101    spfa1;
102    spfa2;
103    for i:=1 to n do
104      if maxp[i]-minp[i]>ans then ans:=maxp[i]-minp[i];
105      writeln(ans);
106 end.

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/brilliant107/p/9471267.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值