前几个月第一次模仿别人的C代码半懂不懂得写了一次裸的最大流:RQNOJ 194:学生运输,算法导论上太多定义、分析、证明了,刚开始看得不是很明白,相比之下数据结构与算法分析有图解,但是没有给出代码。
后来才知道自己写的BFS增广是叫Edmonds-Karp算法,属于Ford-Fulkerson方法,效率是比DFS增广好些,但是也就只能对付RQ上的弱数据,碰到强题还是不行。
最近兴致突发想学习网络流,想学一下dinic,看王欣上的课件模模糊糊有些懂了,但是没有找到适合模仿的代码。
之后找到一份比较全的网络流资料,不但有dinic的分析还有SAP的课件和代码。然后看到zkw的SAP算法心得上讲“事实上,SAP算法更易于理解,时间效率更高,编程更简单,思路更清晰。”,给出的用SAP做USACO中的ditch的代码比我的EK都短,然后就背叛dinic了转去学SAP了。
SAP有当前弧和GAP优化,目前没有尝试过当前弧。
早上做NOI2006最大获利,思路很巧妙,黑书P317有几乎一样的题目的分析。具体的涉及最大闭权子图什么的,自己目前没有能力独立想出来,暂时先放一下吧。
我的RQNOJ194代码:
var a:array[1..1000,1..1000] of longint; q,prev,low:array[1..100000] of longint; m,n,max:longint; procedure init; var i,j,x,y,z:longint; begin readln(m,n); for i:=1 to m do begin read(x,y,z); a[x,y]:=z; end; end; procedure max_flow; var i,j,k,x,head,tail,s,t:longint; begin while true do begin fillchar(low,sizeof(low),0); // fillchar(prev,sizeof(prev),0); I don't konw why can't=0 and change (prev[i]<0)to<>0? for i:=1 to n do prev[i]:=-1; head:=1; tail:=1; q[head]:=1; s:=1; t:=n; low[1]:=maxint; while head<=tail do begin x:=q[head]; inc(head); for i:=1 to n do if (a[x,i]>0)and(prev[i]<0) then begin inc(tail); q[tail]:=i; if low[x]<a[x,i] then low[i]:=low[x] else low[i]:=a[x,i]; prev[i]:=x; end; if prev[t]>0 then break; end; if prev[t]>0 then begin x:=t; inc(max,low[t]); while x<>s do begin a[x,prev[x]]:=a[x,prev[x]]+low[t]; a[prev[x],x]:=a[prev[x],x]-low[t]; x:=prev[x]; end; end else break; end; end; procedure print; begin writeln(max); end; //=============main================= begin init; max_flow; print; end.
我的最大获利代码(80分)
const oo=19930508;//? var vh,h,g,benefit,cost,e,c,opp,next:array[0..400000] of longint; augc,s,t,i,j,k,m,n:longint; flow,tmp,size,x,y,z,tot:longint; found:boolean; procedure addedge(x,y,z:longint); begin inc(size); e[size]:=y; next[size]:=g[x]; g[x]:=size; opp[size]:=size+1; c[size]:=z; inc(size); e[size]:=x; next[size]:=g[y]; g[y]:=size; opp[size]:=size-1; c[size]:=0; end; procedure init; begin readln(n,m); //m:user n:station for i:=1 to n do read(cost[i]); for i:=1 to m do begin readln(x,y,benefit[i]); addedge(i,m+x,oo); addedge(i,m+y,oo); end; s:=0; //s:0 t:n+m+1 1..m:user m+1..m+n:station t:=m+n+1; tot:=t+1; for i:=1 to n do addedge(m+i,t,cost[i]); for i:=1 to m do addedge(s,i,benefit[i]); end; procedure sap(m:longint); var minh,p,augco,i:longint; begin minh:=tot-1; augco:=augc; //backup now augc if m=t then begin found:=true; inc(flow,augc); exit; //found end; p:=g[m]; while p>0 do begin i:=e[p]; if c[p]>0 then begin if h[i]+1=h[m] then//can begin if c[p]<augc then augc:=c[p]; sap(i); //dfs if h[s]>=tot then exit; //no way if found then break; //found augc:=augco; //recover the augc end; if h[i]<minh then minh:=h[i];//adjustment end; p:=next[p]; end; if not found then begin dec(vh[h[m]]); //gap if vh[h[m]]=0 then h[s]:=tot; h[m]:=minh+1; inc(vh[h[m]]); end else begin dec(c[p],augc); inc(c[opp[p]],augc); end; end; procedure main; begin vh[0]:=tot; while h[0]<tot do begin augc:=oo; found:=false; sap(0); end; end; procedure print; begin for i:=1 to m do inc(tmp,benefit[i]); writeln(tmp-flow); end; begin assign(input,'profit.in'); reset(input); assign(output,'profit.out'); rewrite(output); init; main; print; close(output); close(input); end.
因为是模仿别人的,很不理解为什么总节点数是t+1,其实是源点设成0了。增广的过程少打了一个局部变量,DEBUG了一个多小时。很疑惑为什么这个程序能过所有点(不过他数据范围似乎开小了),我和他程序的区别是:我用一个found函数记录有没有找到增广路,而他则是根据返回的增光路流量是不是为0。根据他的程序我改了一下,能AC:
const oo=19930508;//? var vh,h,g,benefit,cost,e,c,opp,next:array[0..400000] of longint; augc,s,t,i,j,k,m,n:longint; flow,tmp,size,x,y,z,tot:longint; found:boolean; procedure addedge(x,y,z:longint); begin inc(size); e[size]:=y; next[size]:=g[x]; g[x]:=size; opp[size]:=size+1; c[size]:=z; inc(size); e[size]:=x; next[size]:=g[y]; g[y]:=size; opp[size]:=size-1; c[size]:=0; end; procedure init; begin readln(n,m); //m:user n:station for i:=1 to n do read(cost[i]); for i:=1 to m do begin readln(x,y,benefit[i]); addedge(i,m+x,oo); addedge(i,m+y,oo); end; s:=0; //s:0 t:n+m+1 1..m:user m+1..m+n:station t:=m+n+1; tot:=t+1; for i:=1 to n do addedge(m+i,t,cost[i]); for i:=1 to m do addedge(s,i,benefit[i]); end; function sap(i,now:longint):longint; var p,minh,tmp,j:longint; begin minh:=tot-1; sap:=0; if i=t then begin inc(flow,now); exit(now); end; p:=g[i]; while p>0 do begin j:=e[p]; if c[p]>0 then begin if h[j]+1=h[i] then begin if c[p]<now then tmp:=sap(j,c[p]) else tmp:=sap(j,now); dec(now,tmp); inc(sap,tmp); dec(c[p],tmp); inc(c[opp[p]],tmp); if h[s]>=tot then exit; if now=0 then break; end; if h[j]<minh then minh:=h[j]; end; p:=next[p]; end; if sap=0 then begin dec(vh[h[i]]); if vh[h[i]]=0 then h[s]:=tot; h[i]:=minh+1; inc(vh[h[i]]); end; end; procedure work; begin vh[0]:=tot; while h[s]<tot do sap(s,oo); end; procedure print; begin for i:=1 to m do inc(tmp,benefit[i]); writeln(tmp-flow); end; begin assign(input,'profit.in'); reset(input); assign(output,'profit.out'); rewrite(output); init; work; print; close(output); close(input); end.