一、最小生成树。
二、最短路。
三、拓扑排序。
四、最大二分匹配。
五、最大流。
六、最小费用最大流。
一、最小生成树。
算法实现和思想和最短路类似。
二、最短路。
1)Floyed算法 O(N3)
简称Floyed(弗洛伊德)算法,是
最简单的最短路径算法,可以计算图中
任意两点间的最短路径。Floyed的时间复杂度是
O (N3),适用于出现
负边权的情况。
memset(maps,127/3,sizeof(maps));
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(maps[i][j]>maps[i][k]+maps[k][j])
maps[i][j]=maps[i][k]+maps[k][j]; //进行更新
2)Dijkstra算法O (N2)
用来计算从一个点到其他所有点的最短路径的算法,是一种
单源最短路径算法。也就是说,
只能计算起点只有一个的情况。
Dijkstra的时间复杂度是
O (N2),它
不能处理存在负边权的情况。
int dij(int s,int t)
{
for(int i=1;i<=n;i++)
p[i]=g[s][i];
memset(vis,false,sizeof(vis));
p[s]=0;vis[s]=true;
while(1)
{
v=-1;
for(int i=1;i<=n;i++)
if(!vis[i]&&(v==-1||p[v]>p[i]))
v=i;
if(v==-1)break;
vis[v]=true;
for(int i=1;i<=n;i++)
if(!vis[i]&&p[i]>p[v]+g[v][i])
p[i]=p[v]+g[v][i];
}
return p[t];
}
3)SPFA算法O(kE)
主要思想是:
初始时将起点加入队列。每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队。直到队列为空时算法结束。
这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法。
struct edge
{
int to,cost;
};
int spfa(int s,int t)
{
memset(is,false,sizeof(is));
memset(d,127/3,sizeof(d));
queue<int> que;
que.push(s);
is[s]=true;
d[u]=0;
while(!que.empty())
{
int u=que.top();que.pop();
for(int i=0;i<g[u].size();i++)
{
edge &e=g[u][i];
if(d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
if(!is[e.to])
{ is[e.to]=true;
que.push(e.to);
}
}
}
}
return d[t];
}
三、拓扑排序。
对一个有向无环图(DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。
void TopSort(int n)
{
num = 0;
for(int i=1;i<=n;i++) //先将没有前驱的节点加入队列
if(!degree[i]&&vis[i])
Q[num++] = i;
for(int i=0;i<num;i++)
{
//删除从该节点出发的所有边,更新degree数组
for(int k = head[Q[i]];k != -1;k = Edge[k].next)
{
degree[Edge[k].to]--;
//如果degree数组为0,说明新的没有前驱的节点找到,加入队列中
if(!degree[Edge[k].to])
Q[num++] = Edge[k].to;
}
}
}
四、最大二分匹配。
结论:
1.最大匹配+最小边覆盖=V (无孤立点)
2..最大独立集+最小顶点覆盖=V (在G中两两互不相连的定点集合)
3.最大匹配=最小顶点覆盖 (对于二分图)
4.最小路径覆盖+最大匹配=V (对于一个DAG图)
5.最大闭合权子图=总正权值-最大流(s连所有正,所有负连t) (点数为s在的最小割)
1)匈牙利算法 O(V*E)
vector<int> g[11111];
int my[33311],n,m;
bool visy[11111];
bool dfs(int x)
{
for(int i=0;i<g[x].size();i++)
{
int y=g[x][i];
if(visy[y])continue;
visy[y]=true;
if(my[y]==-1||dfs(my[y]))
{
my[y]=x;
return true;
}
}
return false;
}
int solve()
{
int ret=0;
memset(my,-1,sizeof(my));
for(int i=1;i<=n;i++)
{
memset(visy,false,sizeof(visy));
if(dfs(i))ret++;
}
return ret;
}
2)HK算法 (优化的匈牙利)O( sqrt(V)*E )
vector<int> g[3003];
int mx[3003],my[3003],dx[3003],dy[3003],dis,n,m;
bool vis[3003];
int bfs()
{
dis=INF;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
queue<int> que;
for(int i=1;i<=n;i++)
if(mx[i]==-1)
{
que.push(i);
dx[i]=0;
}
while(!que.empty())
{
int x=que.front();que.pop();
if(dx[x]>dis)break;
for(int i=0;i<g[x].size();i++)
{ int y=g[x][i];
if(dy[y]!=-1)continue;
dy[y]=dx[x]+1;
if(my[y]==-1)dis=dy[y];
else
{
dx[my[y]]=dy[y]+1;
que.push(my[y]);
}
}
}
return dis!=INF;
}
int dfs(int x)
{
for(int i=0;i<g[x].size();i++)
{int y=g[x][i];
if(vis[y]||dy[y]!=dx[x]+1)continue;
vis[y]=true;
if(my[y]!=-1&&dy[y]==dis)continue;
if(my[y]==-1||dfs(my[y]))
{
my[y]=x;
mx[x]=y;
return 1;
}
}
return 0;
}
int max_macth()
{
int ret=0;
memset(my,-1,sizeof(my));
memset(mx,-1,sizeof(mx));
while(bfs())
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
if(mx[i]==-1&&dfs(i))ret++;
}
return ret;
}
五、二分图最大权完备匹配
1)KM算法 O(V*V*V)
const int INF=0x3f3f3f3f;
const int N=26;
int g[N][N],cx[N],cy[N],n,m,k,lik[N],lack;
bool visx[N],visy[N];
bool find(int x)
{
visx[x]=true;
for(int y=0;y<N;y++)
{
if(visy[y])continue;
int t=cx[x]+cy[y]-g[x][y];
if(!t)
{ visy[y]=true;
if(lik[y]==-1||find(lik[y]))
{
lik[y]=x;
return true;
}
}
else
lack=min(lack,t);
}
return false;
}
int km()
{
memset(lik,-1,sizeof(lik));
memset(cx,0,sizeof(cx));
memset(cy,0,sizeof(cy));
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
cx[i]=max(cx[i],g[i][j]);
for(int i=0;i<N;i++)
{
while(1)
{
lack=INF;
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(find(i))break;
for(int i=0;i<N;i++)
{
if(visx[i])cx[i]-=lack;
if(visy[i])cy[i]+=lack;
}
}
}
int ans=0;
for(int i=0;i<N;i++)
if(lik[i]>=0)
ans+=g[lik[i]][i];
return ans;
}
六、最大流。
1)FF算法
const int INF=0x3f3f3f3f;
struct edge
{
int to,cap, rev;
edge(int x=0,int y=0,int z=0)
{
to=x;cap=y;rev=z;
}
};
vector<edge> g[111];
bool used[111];
int n,m,s,t;
void add(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(int i=0;i<g[v].size();i++)
{
edge &e=g[v][i];
if(used[e.to]||e.cap<=0)continue;
int d=dfs(e.to,t,min(e.cap,f));
if(d>0)
{
e.cap-=d;
g[e.to][e.rev].cap+=d;
return d;
}
}
return 0;
}
int max_flow()
{
int flow=0;
while(1)
{
memset(used,0,sizeof(used));
int f=dfs(s,t,INF);
if(f==0)return flow;
flow+=f;
}
}
2)Dinic 算法 更高效
const int INF=0x3f3f3f3f;
struct edge
{
int to,cap,rev;
edge(int x,int y,int z)
{
to=x;cap=y;rev=z;
}
};
vector<edge> g[5111];
int level[5555],iter[5555],n,m,s,sum;
bool vis[5555];
void add(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(int s)
{
memset(level,-1,sizeof(level));
level[s]=0;
queue<int> que;
que.push(s);
while(!que.empty())
{
int v=que.front();que.pop();
for(int i=0;i<g[v].size();i++)
{
edge &e=g[v][i];
if(e.cap>0&&level[e.to]<0)
{
level[e.to]=level[v]+1;
que.push(e.to);
}
}
}
}
int dfs(int v,int t,int f)
{
if(v==t)return f;
for(int &i=iter[v];i<g[v].size();i++)
{
edge &e=g[v][i];
if(e.cap>0&&level[e.to]>level[v])
{
long long d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
g[e.to][e.rev].cap+=(int)d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
long long flow=0;
while(1)
{
bfs(s);
if(level[t]<0)return flow;
memset(iter,0,sizeof(iter));
long long f;
while(f=dfs(s,t,INF))
{
flow+=f;
}
}
}
七、最小费用最大流。
const int INF=0x3f3f3f3f;
const int MAXN=66;
struct edge
{
int to,cap,cost,rev;
edge(int x,int y,int z,int w)
{
to=x;cost=y;cap=z;rev=w;
}
};
vector<edge> g[MAXN];
int prei[MAXN],prej[MAXN],dist[MAXN],N;
void add(int from,int to,int cost,int cap)
{
g[from].push_back(edge(to,cost,cap,g[to].size()));
g[to].push_back(edge(from,-cost,0,g[from].size()-1));
}
int min_cost_flow(int s,int t,int f)
{
int res=0;
N=t;
while(f>0)
{
// cout<<"*";
for(int i=0;i<=N;i++)
dist[i]=INF;
dist[s]=0;
bool update=true;
while(update)
{
update=false;
for(int i=0;i<=N;i++)
{
if(dist[i]==INF)continue;
for(int j=0;j<g[i].size();j++)
{
edge &e=g[i][j];
if(e.cap>0&&dist[e.to]>dist[i]+e.cost)
{
dist[e.to]=dist[i]+e.cost;
prei[e.to]=i;
prej[e.to]=j;
update=true;
}
}
}
}
if(dist[t]==INF)return -1;
int d=f;
for(int i=t;i!=s;i=prei[i])
{
d=min(d,g[prei[i]][prej[i]].cap);
}
f-=d;
res+=d*dist[t];
for(int i=t;i!=s;i=prei[i])
{
edge &e=g[prei[i]][prej[i]];
e.cap-=d;
g[i][e.rev].cap+=d;
}
}
return res;
}