仅供自己参考。
图论算法:
Dijkstra点对点最短路:
for(i=1;i<=n;i++)
{
min=MAX;
for(j=1;j<=n;j++)
{
if(!mark[j] && dist[j]dist[pos]+map[pos][j]) //start->j or start->pos,pos->j
{
dist[j]=dist[pos]+map[pos][j];//这步跟prim算法有点不同
}
}
}
n代表有多少个点,复杂度是O(n²)。
对dijkstra用优先队列优化则有(用邻接表建图)
void Dijkstra()
{
for(int i = 0; i <= n; i++)
dis[i] = INF;
dis[s] = 0;
priority_queue q;
q.push(node(s, dis[s]));
while(!q.empty())
{
node u = q.top();
q.pop();
for(int i = 0; i < eg[u.point].size(); i++)
{
node v = eg[u.point][i];
if(dis[v.point] > u.val + v.val)
{
dis[v.point] = u.val + v.val;
q.push(node(v.point, dis[v.point]));
front[v.point] = u.point;
}
}
}
}
复杂度为O(E*logV)。E为边数,V为点数。
Bellman—Ford单源最短路算法(可带负权,但不能有负权环)
bool Bellman_Ford()
{
for(int i = 1; i <= nodenum; ++i)
dis[i] = (i == original ? 0 : MAX);
for(int i = 1; i <= nodenum - 1; ++i)
for(int j = 1; j <= edgenum; ++j)
if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost)
{
dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
pre[edge[j].v] = edge[j].u;
}
bool flag = 1; //判断是否含有负权回路
for(int i = 1; i <= edgenum; ++i)
if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)
{
flag = 0;
break;
}
return flag;
}
nodenum就是点的个数,edgenum就是边的个数,从上面这段代码可以明显看出,时间复杂度应该为O(VE+E),V为点的个数,E为边的个数。这种算法有个很严重的问题,就是冗余量太大,进入两个for循环那部分很多时候都是无法操作的。所以有了一种算法叫SPFA,用队列和一个数组标记来去掉那么多冗余的部分。
SPFA单源最短路算法(可带负权,不可有负环)
这种算法的话,复杂度理论上说是O(K*E)k是进队列的次数(一般认为小于等于2),E是边数,他最坏的情况依然会回到Bellman的复杂度。对于稀疏图速度不错,对于稠密图会导致进队列次数增加不建议使用。
bool spfa(int s, int e)
{
int u, v;
for(int i = 0; i <= n; i++)
dis[i] = INF;
memset(flag, 0, sizeof(flag));
dis[s] = 0;
minflow[s] = INF;
flag[s] = 1;
queue q;
q.push(s);
while(!q.empty())
{
u = q.front();
q.pop();
flag[u] = 0;
for(int i = head[u]; ~i; i = front[i])
{
v = to[i];
if(flow[i] && dis[v] > dis[u] + cost[i])
{
dis[v] = dis[u] + cost[i];
par[v] = (make_pair(u, i));
minflow[v] = min(minflow[u], flow[i]);
if(!flag[v])
{
flag[v] = 1;
q.push(v);
}
}
}
}
if(dis[e] == INF)
return 0;
return 1;
}
Floyd-Warshall全局最短路算法:
void floyd_warshall(int n)
{
int i,j,k;
for (k=1;k<=n;k++)
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (mat[i][k] + mat[k][j] < mat[i][j])
mat[i][j] = mat[i][k] + mat[k][j];
}
floyd复杂度很高,n依然是代表有多少个点,时间复杂度高达O(n^3)。这样可以算算,当有1000个点的时候,运算量是1e9,适用范围就很局限了。
该算法其实很像矩阵乘法。
网络流算法中,EK和Dinic看上去复杂度相同,都是O(n^2 * m),但是Dinic递推到某些点就完成了开始回溯,可以省去遍历很多情况,所以比较快,已经够用。
Dinic算法如下:
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAX = 0x3f3f3f3f;
int G[205][205];
int dis[205];
int n, m, ans;
bool bfs()
{
int now;
memset(dis, -1, sizeof(dis));
queue q;
dis[1] = 0;
q.push(1);
while(!q.empty())
{
now = q.front();
q.pop();
for (int i = 1; i <= n; i++)
if (dis[i] < 0 && G[now][i] > 0)
{
dis[i] = dis[now] + 1;
q.push(i);
}
}
if(dis[n] > 0)
return 1;
else
return 0;
}
int find(int x, int low)
{
int i, tmp = 0;
if (x == n)
return low;
for (i = 1; i <= n; i++)
if (G[x][i] > 0
&& dis[i] == dis[x] + 1
&&(tmp = find(i, min(low, G[x][i]))))
{
G[x][i] -= tmp;
G[i][x] += tmp;
return tmp;
}
return 0;
}
int main()
{
int i, j, u, v, flow, tmp;
while (scanf("%d%d", &m, &n)!=EOF)
{
memset(G, 0, sizeof(G));
for (i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &flow);
G[u][v] += flow;
}
ans = 0;
while (bfs())
{
while(tmp = find(1, MAX))
ans += tmp;
}
printf("%d\n", ans);
}
}
最小费用最大流算法:由于费用可以为负数,可以要用spfa。复杂度为O(E * KE),KE是spfa的复杂度,E为点数,就是前面bfs的n*m的复杂度。
bool spfa(int s, int e)
{
int u, v;
for(int i = 0; i <= n; i++)
dis[i] = INF;
memset(flag, 0, sizeof(flag));
dis[s] = 0;
minflow[s] = INF;
flag[s] = 1;
queue q;
q.push(s);
while(!q.empty())
{
u = q.front();
q.pop();
flag[u] = 0;
for(int i = head[u]; ~i; i = front[i])
{
v = to[i];
if(flow[i] && dis[v] > dis[u] + cost[i])
{
dis[v] = dis[u] + cost[i];
par[v] = (make_pair(u, i));
minflow[v] = min(minflow[u], flow[i]);
if(!flag[v])
{
flag[v] = 1;
q.push(v);
}
}
}
}
if(dis[e] == INF)
return 0;
return 1;
}
void Min_Cost_Max_Flow(int s, int e)
{
int ans = 0, p;
while(spfa(s, e))
{
p = e;
while(par[p].first != s)
{
flow[par[p].second] -= minflow[e];
flow[par[p].second^1] += minflow[e];
p = par[p].first;
}
ans += dis[e];
}
cout << ans << endl;
}
匈牙利算法:复杂度O(n*m),每个点都需要去寻找一次匹配,匹配过程最坏的情况就是全部边都要变,所以为n * m。
bool dfs(int u)
{
int v;
for(v = 0; v < vN; v++)
if(G[u][v] && !used[v])
{
used[v] = true;
if(linker[v] == -1 || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
return false;
}
int hungary()
{
int res = 0;
int u;
memset(linker, -1, sizeof(linker));
for(u = 0; u < uN; u++)
{
memset(used, 0, sizeof(used));
if(dfs(u))
res++;
}
return res;
}
对于有权的二分图,匈牙利算法已经不适用,要使用KM算法,这个比较难懂。
KM算法:复杂度O(?)