1. 程式人生 > 其它 >超簡單整合!手把手教你實現音訊編輯能力

超簡單整合!手把手教你實現音訊編輯能力

最大流

P3376 【模板】網路最大流

P4722 【模板】最大流 加強版 / 預流推進

EK 演算法

複雜度:\(O(nm^2)\)

所以,關於 \(\operatorname{EK}\) ,他死了

#define Maxn 205
#define Maxm 5005
#define inf 0x7f7f7f7f
int n,m,sum=0,tot=1;
int pre[Maxn],ds[Maxn],hea[Maxn],ver[Maxm],nex[Maxm],edg[Maxm];
bool vis[Maxn];
bool dfs(int s,int t)
{
	 memset(vis,false,sizeof(vis));
	 queue<int> q; q.push(s),vis[s]=1,ds[s]=inf;
	 while(!q.empty())
	 {
	 	 int cur=q.front(); q.pop();
	 	 for(int i=hea[cur];i;i=nex[i]) if(edg[i] && !vis[ver[i]])
	 	 	 {
	 	 	 	 pre[ver[i]]=i,vis[ver[i]]=true;
	 	 	 	 ds[ver[i]]=min(ds[cur],edg[i]);
	 	 	 	 if(ver[i]==t) return true;
	 	 	 	 q.push(ver[i]);
			 }
	 }
	 return false;
}
void EK(int s,int t)
{
	 while(dfs(s,t))
	 {
	 	 int x=t;
	 	 while(x!=s)
	 	 {
	 	 	 int i=pre[x];
	 	 	 edg[i]-=ds[t],edg[i^1]+=ds[t];
	 	 	 x=ver[i^1];
		 }
		 sum+=ds[t];
	 }
}
EK(s,t);

dinic 演算法

多路增廣:

每次增廣前,我們先用 BFS 來將圖分層,建立殘量網路。設源點的層數為 \(0\) ,那麼一個點的層數便是它離源點的最近距離。

當前弧優化:

如果一條邊已經被增廣過,那麼它就沒有可能被增廣第二次。那麼,我們下一次進行增廣的時候,就可以不必再走那些已經被增廣過的邊 。

實現:

在建立殘量網路的時候對 \(cur[]\) 進行初始化,表示這一輪尋找曾增廣路的 \(tmphead[]\)

for(int i=1;i<=n;i++) cur[i]=hea[i];

之後再每一次尋找增廣路的 \(dinic\) 函式中進行一下操作:

for(int i=cur[u];i && rest;i=nex[i])
{
	 cur[u]=i;
	 ... 
}

最壞複雜度為 \(O(n^2m)\) (一般跑不到這個上界) ,而在二分圖網路中,複雜度可以達到 \(O(\sqrt{n}m)\)

所以,關於 \(\operatorname{Dinic}\) 他快死了 。

模板程式碼:

P3376 【模板】網路最大流

#define inf 0x7f7f7f7f
#define Maxn 205
#define Maxm 5005
int n,m,s=201,t=202,tot=1;
int hea[Maxn],tmphea[Maxn],nex[Maxm<<1],ver[Maxm<<1],edg[Maxm<<1];
int dep[Maxn],que[Maxn],ql,qr;
ll sum;
void add(int x,int y,int d)
{
	 ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot,edg[tot]=d;
	 ver[++tot]=x,nex[tot]=hea[y],hea[y]=tot,edg[tot]=0;
}
bool bfs()
{
	 memset(dep,0,sizeof(dep)),dep[s]=1;
	 que[ql=qr=1]=s;
	 while(ql<=qr)
	 {
	 	 int cur=que[ql++];
	 	 for(int i=hea[cur];i;i=nex[i]) if(edg[i] && !dep[ver[i]])
	 	 	 dep[ver[i]]=dep[cur]+1,que[++qr]=ver[i];
	 }
	 memcpy(tmphea,hea,sizeof(hea));
	 return dep[t];
}
int dinic(int x,int flow)
{
	 if(x==t) return flow;
	 int rest=flow;
	 for(int i=tmphea[x],tmp;i && rest;i=nex[i])
	 {
	 	 tmphea[x]=i;
	 	 if(edg[i] && dep[ver[i]]==dep[x]+1)
	 	 {
	 	 	 if(!(tmp = dinic(ver[i],min(rest,edg[i])))) dep[ver[i]]=0;
	 	 	 edg[i]-=tmp,edg[i^1]+=tmp,rest-=tmp;
		 }
	 }
	 return flow-rest;
}

int flow;
while(bfs()) while(flow=dinic(s,inf)) sum+=1ll*flow;
printf("%lld\n",sum);

ISAP

該優化被稱為 GAP 優化

咕咕咕

Push-Relabel 預流推進演算法

咕咕咕

HLPP 演算法

複雜度:\(O(n^2\sqrt m)\)

咕咕咕


費用流

P3381 【模板】最小費用最大流

EK

也叫做 \(\operatorname{MCMF}\) 演算法

模板程式碼:

#define Maxn 5005
#define Maxm 50005
#define inf 0x7f7f7f7f
int n,m,sum,hua,tot=1;
int hea[Maxn],ver[Maxm*2],nex[Maxm*2],edg[Maxm*2],Cos[Maxm*2];
int pre[Maxn],ds[Maxn],liu[Maxn];
bool inq[Maxn];
bool spfa(int s,int t)
{
	 memset(ds,inf,sizeof(ds)),memset(liu,inf,sizeof(liu)),memset(inq,false,sizeof(inq));
	 queue<int> q; q.push(s),ds[s]=0,inq[s]=true,pre[t]=-1;
	 while(!q.empty())
	 {
	 	 int cur=q.front(); q.pop(),inq[cur]=false;
	 	 for(int i=hea[cur];i;i=nex[i]) if(edg[i]>0 && ds[vis]>ds[cur]+Cos[i] && ds[cur]+Cos[i]>=0)
 	 	 {
	 	 	 liu[ver[i]]=min(liu[cur],edg[i]);
	 	 	 pre[ver[i]]=i,ds[ver[i]]=ds[cur]+Cos[i];
 	 	 	 if(!inq[ver[i]]) inq[ver[i]]=true,q.push(ver[i]);
		 }
	 }
	 return pre[t]!=-1;
}
void EK(int s,int t)
{
	 while(spfa(s,t))
	 {
	 	 int x=t;
	 	 while(x!=s)
	 	 {
	 	 	 int i=pre[x];
	 	 	 edg[i]-=liu[t],edg[i^1]+=liu[t];
	 	 	 x=ver[i^1];
		 }
		 hua+=ds[t]*liu[t];
		 sum+=liu[t];
	 }
}

EK(s,t);
printf("%d %d\n",sum,hua);

dinic(類 dinic 演算法)

只用將 DFS 改為 \(\operatorname{SPFA}\) 就可以了,將記錄深度的 \(d\) 陣列變為 \(ds\) ,選取增廣路的之後判斷改為:

if(... && ds[ver[i]]==ds[u]+Cost[i]) ...

注意加上當前弧優化,複雜度為 \(O(nmf)\) ,其中 \(f\) 為流量 。

模板程式碼:

#define Maxn 5005
#define Maxm 50005
#define inf 0x7f7f7f7f
bool spfa()
{
	 memset(ds,inf,sizeof(ds)),memcpy(tmphea,hea,sizeof(hea));
	 queue<int> q; q.push(s),ds[s]=0,inq[s]=true;
	 while(!q.empty())
	 {
	 	 int cur=q.front(); q.pop(),inq[cur]=false;
	 	 for(int i=hea[cur];i;i=nex[i]) if(edg[i]>0 && ds[ver[i]]>ds[cur]+Cost[i])
	 	 {
	 	 	 ds[ver[i]]=ds[cur]+Cost[i];
	 	 	 if(!inq[ver[i]]) inq[ver[i]]=true,q.push(ver[i]);
		 }
	 }
	 return ds[t]!=inf;
}
int dinic(int x,int flow)
{
	 if(x==t) return flow;
	 int rest=flow; inq[x]=true;
	 for(int i=tmphea[x];i && rest;i=nex[i])
	 {
	 	 tmphea[x]=i;
	 	 if(!inq[ver[i]] && edg[i]>0 && ds[ver[i]]==ds[x]+Cost[i])
	 	 {
	 	 	 int tmp=dinic(ver[i],min(rest,edg[i]));
	 	 	 if(!tmp) ds[ver[i]]=0;
			 sum_cos+=1ll*Cost[i]*tmp,edg[i]-=tmp,edg[i^1]+=tmp,rest-=tmp; 
		 }
	 }
	 inq[x]=false; return flow-rest;
}

int flow;
while(spfa()) while(flow=dinic(s,inf)) sum_liu+=1ll*flow;

最小割

給定一個網路 \(G=(V,E)\) ,源點和匯點為 \(S\)\(T\) ,若刪去邊集 \(E'\subseteq E\) ,使得 \(S\)\(T\) 不連通,則該邊整合為網路的。邊的容量值和最小的割成為該網路的最小割

\(\text{最小割} = \text{最大流}\)