spfa专题

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快排,然后将这个数组作为二分答案的数组。他们两两不干扰。要注意数组后效性!

 var
x,y,z,n,m,b,mi,sum,l,r,mid,blood,ma,ans:int64;
a,w,cost,next,head,q,dis,c:array[..]of int64;
vis,bj:array[..]of boolean;
i,e:longint;
procedure sort(l,r:int64);
var
i,j,x,y:int64;
begin
i:=l;
j:=r;
x:=c[(l+r) div ];
repeat
while c[i]<x do
inc(i);
while x<c[j] do
dec(j);
if not(i>j) then
begin
y:=c[i];
c[i]:=c[j];
c[j]:=y;
inc(i);dec(j);
end;
until i>j;
if l<j then sort(l,j);
if i<r then sort(i,r);
end; procedure add(x,y,z:longint);
begin
inc(e);a[e]:=y;next[e]:=head[x];head[x]:=e;w[e]:=z;
end;
function check(mid:longint):boolean;
var h,t,u,v,i:longint;
begin
h:=;t:=;
fillchar(q,sizeof(q),);
fillchar(vis,sizeof(vis),false);
fillchar(bj,sizeof(bj),false);
q[]:=; vis[]:=true;
for i:= to n do dis[i]:=<<;
for i:= to n do if mid<cost[i] then bj[i]:=true;
dis[]:=;
while h<t do
begin
inc(h);
u:=q[h];
i:=head[u];
vis[u]:=false;
while i> do
begin
v:=a[i];
// if mid= then writeln(u,' ',v);
if (dis[u]+w[i]<dis[v])and(not bj[v]) then
begin
dis[v]:=dis[u]+w[i];
if (not vis[v])and(not bj[v]) then
begin
vis[v]:=true;
inc(t);
q[t]:=v;
end;
end;
i:=next[i];
end;
end;
if dis[n]>b then exit(false) else exit(true);
end;
begin
readln(n,m,b);
mi:=maxlongint;
for i:= to n do
begin
readln(cost[i]);
if ma<cost[i] then ma:=cost[i];
if mi>cost[i] then mi:=cost[i]; end;
c:=cost;
sort(,n);
for i:= to m do
begin
readln(x,y,z);
add(x,y,z);
add(y,x,z);
end;
l:=; r:=n;
if not check(maxlongint) then
begin writeln('AFK'); exit; end; while l<=r do
begin
mid:=(l+r)div ;
// blood:=b; if check(c[mid]) then
begin ans:=c[mid]; r:=mid-; end else l:=mid+;
end; writeln(ans);
end.

2 最优贸易noip2009

题解:

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

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

开始说正事。

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

 var
e,ie,x,y,z,u,h,t,n,m,i,j,k,ans,v:longint;
head,b,next,ihead,inext,a,ib,maxp,minp,q:array[..]of longint;
vis:array[..]of boolean;
function max(x,y:longint):longint;
begin
if x>y then exit(x) else exit(y);
end;
function min(x,y:longint):longint;
begin
if x>y then exit(y) else exit(x);
end;
procedure add(x,y:longint);
begin
inc(e);b[e]:=y;next[e]:=head[x];head[x]:=e;
end;
procedure iadd(x,y:longint);//反向建边
begin
inc(ie);ib[ie]:=y;inext[ie]:=ihead[x];ihead[x]:=ie;
end;
procedure spfa1;
var i,j,h,t,u,v:longint;
begin
for i:= to n do minp[i]:=maxlongint;
h:=;t:=; q[]:=;vis[]:=true;
minp[]:=a[];
while h<t do
begin
inc(h);
u:=q[h];
vis[u]:=false;
i:=head[u];
while i> do
begin
v:=b[i];
if minp[v]>minp[u] then
begin
minp[v]:=minp[u];
minp[v]:=min(minp[v],a[v]);
if not vis[v] then
begin
vis[v]:=true;
inc(t);
q[t]:=v;
end;
end;
i:=next[i];
end;
end; end;
procedure spfa2;
var i,j,h,t,u,v:longint;
begin
fillchar(q,sizeof(q),);
fillchar(vis,sizeof(vis),false);
h:=;t:=;
q[]:=n; vis[n]:=true;
maxp[n]:=a[n];
while h<t do
begin
inc(h);
u:=q[h];
i:=ihead[u];
vis[u]:=false;//打在前面有负环也能运行
while i> do
begin
v:=ib[i];
if maxp[v]<maxp[u] then // you wrong here
begin
maxp[v]:=maxp[u];
maxp[v]:=max(maxp[v],a[v]);
if not vis[v] then
begin
vis[v]:=true;
inc(t);
q[t]:=v;
end;
end;
i:=inext[i]; end;
end; end;
begin
readln(n,m);
for i:= to n do
read(a[i]);
for i:= to m do//m 不要再打成n了!
begin
readln(x,y,z);
add(x,y);
iadd(y,x);
if z= then
begin
add(y,x);
iadd(x,y);
end;
end;
spfa1;
spfa2;
for i:= to n do
if maxp[i]-minp[i]>ans then ans:=maxp[i]-minp[i];
writeln(ans);
end.
上一篇:怎样使用ServletContextListener接口


下一篇:Java开源库