【最大流 模板 Dinic】POJ 1459 Power Network
阿新 • • 發佈:2018-12-24
Problem Description
給你n, np, nc, m。分別代表有n個點,其中np個是發電站,nc個是消費者,剩下n-np-nc個就是中轉站。接下來給你m條邊,每條邊格式(u,v)w。代表u點到v點這條線路最大能夠運輸w的電。最後給你np個點最大能夠輸出的電,nc個點最大能夠接收的電(格式:(u)w, u這個點能夠輸出(接收)最大的電,發電站的點是輸出,消費者的點是接收)。
思路:
所有發電站看成一個超級源點s,所有消費者看成一個超級匯點t。如何實現,從s向每個源點連一條容量為對應最大流出容量的邊,從每個匯點向t連一條容量為對應最大流入容量的邊。然後求s->t的最大流即可。
給了兩種形式的Dinic。存圖都用了鄰接矩陣,這樣可以好觀察出兩種Dinic的那些細節不一樣 時間複雜度O(|E||V|^2)
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int mm = 200;
const int inf = 0x3f3f3f3f;
int Map[mm][mm], n;//存圖
int dis[mm];//記錄mm到超級源點的最短距離
void bfs(int s, int t)//求dis[]陣列
{
memset(dis, -1, sizeof(dis));
dis[s] = 0;
queue<int> q;
q.push(s);
while(!q.empty())
{
s = q.front(); q.pop();
for(int i = 1; i <= t; i++)
{
if(dis[i] == -1 && Map[s][i])//容量不為0
{
dis[i] = dis[s] + 1 ;
q.push(i);
}
}
}
}
int dfs(int s, int t, int f)//s為當前點,t為超級匯點,f為當前流入s點的流量
{
if(s == t) return f;//代表到達超級匯點,直接返回當前流到匯點的流量
int ans = 0;//用來記錄當前點,輸出的最大流量
for(int i = 1; i <= t; i++)
{
if(Map[s][i] && dis[i] > dis[s])
{
int d = dfs(i, t, min(f, Map[s][i]));
if(d > 0)//代表到達了匯點,到達匯點的這條增廣路,最小的流量(限制流量)
{
Map[s][i] -= d;//正向邊-d
Map[i][s] += d;//反向邊+d
ans += d;//更新當前點的,輸出的最大流量
f -= d;//到達當前點的流量-d
if(!f) break;//如果當前點已經沒有可以輸出的流量,退出迴圈
}
}
}
if(ans) return ans;//當前點,輸出的最大流量有的話
dis[s] = -1;//沒有就標記-1,再到這點也沒意義了。
return 0;
}
int Dinic(int s, int t)
{
int Max_flow = 0, flow;
for(;;){
bfs(s, t);//先跑bfs
if(dis[t] < 0) break;//代表到達不了t,直接退出迴圈
Max_flow += dfs(s, t, inf);//更新最大流
}
return Max_flow;
}
int main()
{
int np, nc, m, u, v, w, i;
char s[20];
while(~scanf("%d %d %d %d", &n, &np, &nc, &m))
{
memset(Map, 0, sizeof(Map));//初始化
while(m--)//建圖
{
scanf("%s", s);
sscanf(s, "%*c%d%*c%d%*c%d", &u, &v, &w);
Map[u + 1][v + 1] = w;
}
for(i = 0; i < np; i++)
{
scanf("%s", s);
sscanf(s, "%*c%d%*c%d", &u, &w);
Map[0][u + 1] = w;
}
n = n + 1;
for(i = 0; i < nc; i++)
{
scanf("%s", s);
sscanf(s, "%*c%d%*c%d", &u, &w);
Map[u + 1][n] = w;
}
//0為超級源點,n為超級匯點
printf("%d\n", Dinic(0, n));
}
return 0;
}
第二種型別主要就標識一下,那些地方不一樣。就不那麼詳細的註釋了
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int mm = 200;
const int inf = 0x3f3f3f3f;
int Map[mm][mm], n;
int dis[mm], iter[mm];//多了個iter[]陣列,作用當前弧,在其之前的邊已經沒有用了。
void bfs(int s, int t)
{
memset(dis, -1, sizeof(dis));
dis[s] = 0;
queue<int> q;
q.push(s);
while(!q.empty())
{
s = q.front(); q.pop();
for(int i = 1; i <= t; i++)
{
if(dis[i] == -1 && Map[s][i])
{
dis[i] = dis[s] + 1;
q.push(i);
}
}
}
}
int dfs(int s, int t, int f)
{
if(s == t) return f;
for(int &i = iter[s]; i <= t; i++)//i是取iter[s]地址
//下一次dfs的時候就可以直接跳過之前s為起點,訪問過的邊,這就是iter[]的作用,避免浪費的搜尋
{
if(i == 0) continue;//從1點開始搜尋
if(Map[s][i] && dis[i] > dis[s])
{
//這裡是直接找到增光路,然後一路返回到超級源點,然後退出迴圈,這時候你就能理解呼叫的時候為什麼是迴圈,iter[]陣列為何要這樣了。
int d = dfs(i, t, min(f, Map[s][i]));
if(d > 0)
{
Map[s][i] -= d;
Map[i][s] += d;
return d;
}
}
}
return 0;
}
int Dinic(int s, int t)
{
int Max_flow = 0, flow;
for(;;){
bfs(s, t);
if(dis[t] < 0) break;
memset(iter, 0, sizeof(iter));//初始化為0,但是是從1點開始搜,所以一會兒加個continue就可以了
while((flow = dfs(s, t, inf)) > 0)//不一樣的點。這裡是迴圈,一會兒解釋為什麼
{
Max_flow += flow;
}
}
return Max_flow;
}
int main()
{
int np, nc, m, u, v, w, i;
char s[20];
while(~scanf("%d %d %d %d", &n, &np, &nc, &m))
{
memset(Map, 0, sizeof(Map));
while(m--)
{
scanf("%s", s);
sscanf(s, "%*c%d%*c%d%*c%d", &u, &v, &w);
Map[u + 1][v + 1] = w;
}
for(i = 0; i < np; i++)
{
scanf("%s", s);
sscanf(s, "%*c%d%*c%d", &u, &w);
Map[0][u + 1] = w;
}
n = n + 1;
for(i = 0; i < nc; i++)
{
scanf("%s", s);
sscanf(s, "%*c%d%*c%d", &u, &w);
Map[u + 1][n] = w;
}
printf("%d\n", Dinic(0, n));
}
return 0;
}