前幾個月第一次模仿別人的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.