網(wǎng)絡(luò)流與dinic/Fulkerson模板以及相關(guān)題
膜板題 ->排水溝poj1273
網(wǎng)絡(luò)流弄了將近一個月,寒假玩v家去了導(dǎo)致信息競賽進度不大,不過還是先總結(jié)一下吧
一 網(wǎng)絡(luò)流滿足三個性質(zhì):
1 容量限制
每條邊不能夠提供大于其流量的邊,(反向邊要加上對應(yīng)的增廣流量是為了滿足流量守恒)
2 對稱性
f(u,v)=-f(v,u)
3 流量守恒
所有點的流入量等于流出量
通俗點說就是你不能從1號城市運3個冰球到2號,而運到2號的再傳到3號城市的冰球變成了1個(好吧很意義不明)
二 增廣路
能走的路一定是還有流量的路,
而增廣路是從s->t還能多增加流的路。
三 交替路
二分圖從一端到另一端,走一邊到另一邊再到原來的一邊的非同一個頂點的路是交替路(比較通俗)
用vector模擬鄰接表
struct edge
{
int to,cap,rev;
};
其中cap是剩余容量,rev是反向邊(和劉汝佳方法不一樣)推薦白書上那種 方便
關(guān)于兩個模板,fulkerson(我老是打錯成**)和dinic算法
我們先來說一下fulkerson算法吧
用dfs找增廣路 為了不走回頭路,我們使用數(shù)組標記
一條路能走當(dāng)且僅當(dāng)它的剩余容量大于0,那么我們只需要不斷地沿著路dfs就行了,唯一注意的是,我們要在增廣一條路后去掉對應(yīng)的容量 并把反向邊加上容量(根據(jù)容量守恒原則)
沒什么很難的地方
代碼 可以練好后交poj1273
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=500;
const int inf=0x3f3f3f3f;
struct edge
{
int to;
int cap,rev;
void ini()
{
to=0;
cap=0;
rev=0;
}
};
vector g[maxn];
bool used[maxn];
int s,t,n,m;
int ans=0;
void addedge(int from,int to,int cap)
{
g[from].push_back((edge){to,cap,g[to].size()});
g[to].push_back((edge){from,0,g[from].size()-1});
}
int dfs(int v,int t,int f)
{
if(v==t) return f;
used[v]=true;
for(unsigned i=0;i0)
{
int d;
d=dfs(buffer.to,t,min(f,buffer.cap));
if(d>0)
{
g[v][i].cap-=d;
g[buffer.to][buffer.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int st,int ed)
{
ans=0;
while(1)
{
memset(used,0,sizeof(used));
int buff=dfs(st,ed,inf);
if(buff==0) return ans;
else
{
ans+=buff;
}
}
}
int main()
{
while(scanf("%d%d",&m,&n)==2)
{
s=1;
t=n;
for(int i=0;i
dinic算法
復(fù)雜度O(|V||V||E|)
上界比較松
將圖分層,
對于一個圖,每一次增廣一定有一條路會斷(即沒有剩余容量)
這時我們記一個dis 數(shù)組來記起點到各個點的距離(我們暫時只考慮所有cost為1的情況)
這樣可以保證不對一條沒用的邊進行多次檢查,節(jié)省了時間,其余與深搜類似
#include
#include
#include
#include
#include
#include
#include
#include
//手寫隊列
//AC
using namespace std;
const int maxn=5000000;
const int inf=0x3f3f3f3f;
struct edge
{
int to;
int cap;
int rev;
};
vector g[maxn];
int dis[maxn];
int cur[maxn];
int q[maxn];
int s,t,n,m;
int res=0;
int readint()
{
char c;
int ans=0;
while(c=getchar())
{
if(isdigit(c))
{
ans=ans*10+c-'0';
}
else c=getchar();
}
return ans;
}
void addedge(int from,int to,int cap)
{
g[from].push_back((edge){to,cap,g[to].size()});
g[to].push_back((edge){from,0,g[from].size()-1});
}
void bfs()
{
memset(dis,-1,sizeof(dis));
memset(q,0,sizeof(q));
int hd=0;
int tl=0;
q[hd]=s;
dis[s]=0;
while(hd<=tl)
{
int head=q[hd];
hd++;
for(unsigned i=0;i0 && dis[e.to]<0)
{
dis[e.to]=dis[head]+1;
tl++;
q[tl]=e.to;
}
}
}
}
int dfs(int v,int ed,int flow)
{
int f;
if(v==ed)
{
return flow;
}
else
{
for(int &i=cur[v];i0 && dis[e.to]==dis[v]+1)
{
f=dfs(e.to,ed,min(flow,e.cap));
if(f>0)
{
e.cap-=f;
g[e.to][e.rev].cap+=f;//注意結(jié)構(gòu)體后要帶成員
return f;
}
}
}
}
return 0;
}
int max_flow(int st,int ed)
{
int ans=0;
while(1)
{
bfs();
if(dis[t]<0) return ans;
else
{
memset(cur,0,sizeof(cur));
int f;
while((f=dfs(st,ed,inf))>0)
{
ans+=f;
}
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
res=0;
for(int i=0;i
二分圖匹配的網(wǎng)絡(luò)流變形
對于一個二分圖,我們想到如果將最大匹配轉(zhuǎn)化為最大流就好了。
那么如何轉(zhuǎn)化呢?對于一個匹配,一定是數(shù)條沒有公共頂點的邊,那么就想到了數(shù)條這樣相同流的增廣路
我們添加一個源點和匯點,它們分別向兩邊的兩點各連一條容量為1,cost=0的邊,這樣就轉(zhuǎn)化為了最大流來求這些邊的問題了
#include
#include
#include
#include
#include
#include
//AC
using namespace std;
const int maxn=10000;
const int inf=0x3f3f3f3f;
int dis[maxn];
int cur[maxn];
int s,t,n,m,e;
struct edge
{
int to,cap,rev;
};
vector g[maxn];
void addedge(int from,int to,int cap)
{
g[from].push_back((edge){to,cap,g[to].size()});
g[to].push_back((edge){from,0,g[from].size()-1});
}
//dinic
bool bfs(int st,int ed)
{
memset(dis,inf,sizeof(dis));
int q[maxn];
int head=0;
int tail=0;
q[head]=st;
dis[st]=0;
while(head<=tail)
{
int h=q[head];
head++;
for(unsigned i=0;i0 && dis[e.to]==inf)
{
dis[e.to]=dis[h]+1;
tail++;
q[tail]=e.to;
}
}
}
return (dis[ed]!=inf);
}
int dfs(int v,int ed,int leftflow)
{
if(leftflow==0 || v==ed)
{
return leftflow;
}
else
{
for(int &i=cur[v];i0 && dis[e.to]==dis[v]+1)/////
{
int f=dfs(e.to,ed,min(leftflow,e.cap));
if(f>0)
{
e.cap-=f;///////////////////
g[e.to][e.rev].cap+=f;///////////////
return f;
}
}
}
return 0;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&e);
s=0;
t=n+m+1;
memset(dis,inf,sizeof(dis));
memset(cur,0,sizeof(cur));
for(int i=0;i=1 && from<=n && to>=1 && to<=m)
{
to+=n;
addedge(from,to,1);
}
}
for(int i=1;i<=n;i++)
{
addedge(s,i,1);
}
for(int j=n+1;j<=n+m;j++)
{
addedge(j,t,1);
}
int ans=0;
while(bfs(s,t))
{
int f;
memset(cur,0,sizeof(cur));/////////////
while((f=dfs(s,t,inf))>0)
{
ans+=f;
}
}
printf("%dn",ans);
return 0;
}
固定流最小費用流問題
給你10個冰球,你要運完,但是你要經(jīng)過長度最小的道路
如何做呢?這里推薦一個用連續(xù)最短路的貝爾曼福德實現(xiàn)的算法:
流固定,每次以最短路增廣,從流中減去對應(yīng)流
這樣寫看上去比較好懂的
int min_cost_flow(int st,int ed,int f)
{
int ans=0;
while(f>0)
{
bool update=true;
memset(dis,inf,sizeof(dis));
dis[st]=0;
while(update)
{
update=false;
for(int i=0;i<=n+m+1;i++)
{
if(dis[i]!=inf)
{
for(unsigned j=0;j0 && dis[e.to]>dis[i]+e.cost)
{
dis[e.to]=dis[i]+e.cost;
prevv[e.to]=i;
preve[e.to]=j;
update=true;
}
}
}
}
}
if(dis[ed]==inf)
{
return -1;
}
int d=inf;
for(int i=ed;i!=st;i=prevv[i])
{
d=min(d,g[prevv[i]][preve[i]].cap);
}
f-=d;
ans+=dis[ed]*d;
for(int i=ed;i!=st;i=prevv[i])
{
edge & e=g[prevv[i]][preve[i]];
e.cap-=d;
g[i][e.rev].cap+=d;
}
}
return ans;
}
應(yīng)用當(dāng)然不止這些 最主要是建模,這里有些題(書上的例題)
poj1273排水溝
poj3281奶牛的餐飲
poj3686玩具
luogu3386二分圖匹配模板