C語言之switch語句
網路流
網路:有向圖,有一個入度為0的點,有一個出度為0的點
源點:入度為0的點s
匯點:出度為0的點t
容量:每條邊的權值c(u,v)=3
流量:f(s,a)<=c(u,v)(容量限制(Capacity Constraint):對所有頂點對 u, v ∈ V,要求 f(u, v) ≤ c(u, v)。
)
反對稱性(Skew Symmetry):對所有頂點對 u, v ∈ V,要求 f(u, v) = - f(v, u)。
流入量:對於c來說 f(a,c)+f(b,c)
|| 流守恆性
:任意u,存在 ∑ f(i,u)= ∑ f(u,j)
流出量:對於c來說 f(c,t)
總流量:|f|= ∑ f(s,v)(與s直接相連的點)
最小流:一定是0
最大流:著重討論的問題: 給出源點 s 和匯點 t 的流網路 G,希望找出從 s 到 t 的最大值流。
割:斷開後可以使s與t不連通的邊的集合 |cut|=割的邊值之和
最大割:等於全部邊值之和
最小割:著重要討論的問題
費用流:在最大流的情況下討論費用
殘留網路: 殘留量(c'(u,v)=c(u,v)-f(u,v))組成的網路
增廣路:能夠增加流量的路(s-t)
限制條件: 經過邊的流不能超過邊的容量;
除了源點 s 和匯點 t,對於其它所有頂點,流入量與流出量要相等。
FF演算法
Ford-Fulkerson 演算法( 即增廣路方法 簡稱FF方法 )是一種解決最大流的方法,其依賴於三種重要思想:
殘留網路(Residual networks)
增廣路徑(Augmenting paths)
割(Cut)
網路達到最大流當且僅當殘留網路中沒有增廣路
HDU - 1532 https://vjudge.net/problem/HDU-1532
#include <queue> #include <cstring> #include <cstdio> #include <iostream> using namespace std; #define maxn 205 #define INF 1e9 //最大流板子 int n,m,a,b,cost,map[maxn][maxn];//儲存網路 int max_flow(int num,int map[][maxn],int s,int t)//最大流函式(結點數,圖,源點,匯點) { int p[maxn],min_flow[maxn],flow[maxn][maxn],ans = 0 ;//記錄結點的父節點 當前路徑中最小的一段的值(限制值) 記錄當前網路中的流 結果 memset(flow,0,sizeof(flow)); while(1)//一直迴圈,直到不存在增廣路徑 { queue<int> q; memset(p,-1,sizeof(p)); q.push(s); p[s] = -2;//源點父節點 min_flow[s] = INF; while(!q.empty())//BFS 尋找增廣路徑 { int temp = q.front();//出列 q.pop(); for(int i = 0 ; i < num ; i++)//從temp往外擴充套件 { if(p[i] == -1 && flow[temp][i] < map[temp][i]) //當結點i還未被探索到且還有可用流量 { q.push(i);//加入佇列 p[i] = temp;//父節點 min_flow[i] = min(min_flow[temp],map[temp][i]-flow[temp][i]); } } if(p[t]!=-1)//t的父節點不=初始值,說明已經BFS到了一條路徑 { int k=t; while(p[k] >= 0) {flow[p[k]][k] +=min_flow[t]; flow[k][p[k]] -=min_flow[t];k = p[k];}//新流量加入flow break; } } if(p[t] != -1)ans+=min_flow[t]; else return ans;//若不存在增廣路徑則返回 } } int main() { //n流通數 m節點數 while(cin>>n>>m) { memset(map,0,sizeof(map));//清空 for(int i=0;i<n;i++) { cin>>a>>b>>cost; map[a-1][b-1]+=cost;//存圖 } cout<<max_flow(m,map,0,m-1)<<endl; } return 0; }
EK演算法
Edmonds-Karp演算法 即最短路徑增廣演算法 簡稱EK演算法
在原先每條邊的基礎上,加上相同容量的反向邊
BFS找增廣路:找到:更新最大流+更新參與網路
找不到:退出
不斷的找最短路 找的方法就是每次找一條邊數最少的增廣 也就是最短路徑增廣
HDU - 1532 https://vjudge.net/problem/HDU-1532
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdlib>
#include<stdio.h>
#include<cstring>
using namespace std;
const int maxn=220;
const int inf=1e9;
//EK演算法
int n,m,mp[maxn][maxn],flow[maxn],pre[maxn];//n點數 m邊數 map存圖 flow表示當前流量 pre儲存前一條增廣路
queue<int> q;//因為要用BFS所以需要佇列
int bfs(int s,int t)
{
while(!q.empty())q.pop();
memset(pre,-1,sizeof(pre));
pre[s]=0;flow[s]=inf;
q.push(s);
while (!q.empty())
{
int p=q.front();q.pop();
if(p==t)//走到匯點
break;
for(int i=1;i<=n;i++)//找邊
{
if(i!=s&&mp[p][i]>0&&pre[i]==-1)
{
pre[i]=p;
flow[i]=min(flow[p],mp[p][i]);
q.push(i);
}
}
}
if(pre[t]==-1)return -1;
return flow[t];//流量是增加的差值
}
int EK(int s,int t)
{
int delta=0,tol=0;
while (1)
{
delta=bfs(s,t);
if(delta==-1)break;
int p=t;
while(p!=s)
{
mp[pre[p]][p]-=delta;
mp[p][pre[p]]+=delta;
p=pre[p];
}
tol+=delta;
}
return tol;
}
int main()
{
while(cin>>m>>n)
{
memset(mp,0,sizeof(mp));
memset(flow,0,sizeof(flow));
for(int i=0;i<m;i++)
{
int u,v,w;cin>>u>>v>>w;
mp[u][v]+=w;
}
cout<<EK(1,n)<<endl;
}
return 0;
}
最小費用最大流問題
通過EK,Dinic,ISAP演算法可以得到網路流圖中的最大流,一個網路流圖中最大流的流量max_flow是唯一的,但是達到最大流量max_flow時每條邊上的流量分配f是不唯一的。
如果給網路流圖中的每條邊都設定一個費用cost,表示單位流量流經該邊時會導致花費cost。那麼在這些流量均為max_flow的流量分配f中,存在一個流量總花費最小的最大流方案。
即 min{sum(cost(i, j)*f(i,j) | (i, j)屬於方案f中的邊, f(i,j)為 邊(i,j)上的流量, f為某一個最大流方案}。此即為最小費用最大流
。
最小費用最大流問題與演算法實現(Bellman-Ford、SPFA、Dijkstra)
例題:
POJ - 2135 https://vjudge.net/problem/POJ-2135
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<iostream>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
#define eps 1e-8
using namespace std;
const int MAXN = 10000;
const int inf = 0x3f3f3f3f;
struct Edge {
int to,next,cap,flow,cost;
} edge[100000];
int N,head[MAXN],tol,pre[MAXN],dis[MAXN];
bool vis[MAXN];
void adde(int u,int v,int cap,int cost)
{
edge[tol].to = v;edge[tol].cap = cap;edge[tol].cost = cost;edge[tol].flow = 0;edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;edge[tol].cap = 0;edge[tol].cost = -cost;edge[tol].flow = 0;edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s,int t)
{
queue<int>q;
for(int i = 0; i < N+1; i++){dis[i] = inf;vis[i] = false;pre[i] = -1;}
dis[s] = 0;vis[s] = true;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost )
{
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t] == -1)return false;
else return true;
}
int mincostmaxflow(int s,int t,int &cost) {
int flow = 0;
cost = 0;
while(spfa(s,t)) {
int Min = inf;
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
if(flow==2)
return flow;
}
return flow;
}
int main()
{
int n,m;
cin>>n>>m;
N = n,tol = 0;
memset(head,-1,sizeof(head));
for(int i=0;i<m;i++)
{
int u,v,w;
cin>>u>>v>>w;
adde(u,v,1,w);
adde(v,u,1,w);
}
int c;
mincostmaxflow(1,n,c);
cout<<c<<endl;
return 0;
}
//最大費用最小流只要在新增邊的時候換一下位置就好了
//求最大費用最大流只需要把費用換成相反數,用最小費用最大流求解即可