什麼是演算法面試02:演算法面試的準備範圍
阿新 • • 發佈:2021-11-22
前置知識
就是在最大流的情況下求最小的花費(而最大費用最大流就可以把費用取成負數在跑最小費用最大流)。
於是我們就想到一個思路,在找增廣路徑的時候把找最短的增廣路徑改成找費用最小的增廣路徑。
正確的思路,就是這樣的。
接下來就有兩個思路
ZKW費用流
由NOI2008年金牌選手張昆瑋發明
solution
首先我們先設個dis值,跟sap裡的d值有點像,但這裡的dis[i]表示的是終點到i點最短路徑。
然後我們可以直接按照dis值找最短路徑,比如說我想知道從i點走到j點是否是最短路徑,就當dis[j]+i點到j點的距離==dis[j]的時候,這裡走的一定是最短路徑,否則就不是。
當找不到增廣路的時候,說明你的dis值已經有問題了,於是我們考慮如何更改dis值。
我們先把節點分為兩個部分,一個是S部,一個是T部。
S部就是從源點能靠dis值走到的點,剩下的點就是T部。
首先我們知道,我們將S部的點的dis值全部加上一個值,S部的性質還是滿足的。
所以說,我們只需要找到一個值,使得S部加上這個值後,能與T部相連。
因為我們求的是最短路徑,所以說我們就找一個S部的點到T部的點是dis需要增加的最小值。
然後我們接著跑最大流,直到從源點無法到匯點,也就是說,S部的點永遠不可能與T部的點相連的時候,就找到答案了。
如何記錄答案,就是將你增廣的量乘上你一路上經過的費用的和,也就是從源點到匯點的最短路乘上增廣量。
code
bool label() { int mindis=INF; for(int i=1;i<=n;i++) if(vis[i]) for(int j=last[i];j;j=nxt[j]) if(!vis[to[j]]&&a[j]) mindis=min(w[j]+dis[to[j]]-dis[i],mindis);//求最小差值 if(mindis==INF) return false;//如果沒有一個邊能從S部流向T部,就說明沒有個增廣路徑了 for(int i=1;i<=n;i++) if(vis[i]) dis[i]+=mindis;//將S部的點的dis全部加上一個值 return true; } int aug(int s,int augcc) { if(s==n) { ans+=dis[1]*augcc;//將增廣量乘於一路上的費用的和就是你所花費的代價 return augcc; } int augc=augcc,delta,son; vis[s]=1; for(int i=last[s];i&&augc;i=nxt[i]) { son=to[i]; if(a[i]>0&&!vis[son]&&dis[s]==dis[son]+w[i]) { delta=aug(son,min(a[i],augc)); a[i]-=delta,a[i^1]+=delta;//i^1是反向邊,邊是從2號邊開始儲存的 augc-=delta; } } return augcc-augc; } void augca() { do { do { memset(vis,0,sizeof(vis)); }while(aug(1,INF)); }while(label());//在找不到增廣路徑的情況下更新dis值 }
用SPFA找最短路徑
solution
就是在每次找增廣路徑前,用SPFA求一遍最短路,然後在用最短路增廣。
我們用dis值記錄最短路,然後按照zkw費用流增廣的方法增廣。
也就是說,每次不合法的時候我們並不是在原有的dis值的基礎上更改dis值,而是重新算一遍dis值。
code
bool spfa() { queue<int> myq; myq.push(target); memset(dis,0x3f,sizeof dis); dis[target]=0,inq[target]=1; while(!myq.empty()) { int t=myq.front(); myq.pop(),inq[t]=0; for(int i=fir[t];i;i=e[i].next) { int v=e[i].v; if(e[i^1].cap>0) if(dis[v]>dis[t]+e[i^1].cost) { dis[v]=dis[t]+e[i^1].cost; if(inq[v]==0) myq.push(v),inq[v]=1; } } } if(dis[src]==0x3f3f3f3f)return 0; else return 1; } int aug(int s,int augco) { if(s==target) { ans+=dis[src]*augco; return augco; } int tempcap=augco,delta; for(int i=fir[s];i&&tempcap>0;i=e[i].next) { if(!vis[e[i].v]&&e[i].cap>0) { if(dis[e[i].v]==dis[s]-e[i].cost) { vis[e[i].v]=1; delta=aug(e[i].v,min(e[i].cap,tempcap)); vis[e[i].v]=0; tempcap-=delta; e[i].cap-=delta; e[i^1].cap+=delta; } } } return augco-tempcap; }