SPFA专题
1通往奥格瑞玛的道路
在艾泽拉斯,有n个城市。编号为1,2,3,...,n。
城市之间有m条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。
每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。
假设1为暴风城,n为奥格瑞玛,而他的血量最多为b,出发时他的血量是满的。
歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。
输入输出格式
输入格式:
第一行3个正整数,n,m,b。分别表示有n个城市,m条公路,歪嘴哦的血量为b。
接下来有n行,每行1个正整数,fi。表示经过城市i,需要交费fi元。
再接下来有m行,每行3个正整数,ai,bi,ci(1<=ai,bi<=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≤200,m≤10000,b≤200
对于100%的数据,满足n≤10000,m≤50000,b≤1000000000
对于100%的数据,满足ci≤1000000000,fi≤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.
2 最优贸易noip2009
题解:
就因为我for 中m 打错成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.