1、 最短路算法
- Bellman-Ford算法
- Dijkstra算法
- SPFA算法
- Floyd算法
- 被气死的WA
2、最小生成树算法
- Prim算法
- Kruskal算法
- 被气死的WA
1、单源最短路(Bellman-Ford算法)
描述:
思想为连续对每条边进行松弛操作,在每次松弛时把每条边都更新一下,若在V-1次松弛后还能更新,则说明图中有负环。可以求含负权图及判定负环的最短路算法。
复杂度:
O(VE)
// Bellman-Ford算法
struct edge{ //从顶点from指向顶点to权值为cost的边
int from,to,cost;
}es[Max_E];
int V,E;
int d[Max_V];
void Bellman_Ford(int s){
memset(d,0x3f,sizeof(d));
d[s]=0;
//int k=0; //用于判定负环
while(true){
bool flag=false;
for(int i=0;id[e.from]+e.cost){
d[e.to]=d[e.from]+e.cost;
flag=true;
}
}
if(!flag)break;
//if(V==++k){printf("存在负环\n");return;}
}
}
**备注:
**有向图中只要含有一个权值和为负数的有向闭合路径,称图含有负环;无向图只要含有一条边权值为负,就称含有负环。
2、单源最短路(Dijkstra算法)
描述:
思想为运用贪心策略(没有负边),不断从已经确定最短距离的顶点集合(开始只有起点s)向外找与该顶点集合距离最近的顶点,加入到该集合并更新与该顶点相连顶点的最短距离。
复杂度:
邻接表+优先队列 O(ElogV) // 有负边的话
// Dijkstra算法
typedef pair P;
struct edge{
int to,cost; //first是最短距离,second是顶点的编号
edge(int t,int c):to(t),cost(c){};
};
int V,E;
int d[Max_v];
vectorG[Max_v];
void Dijkstra(int s){
memset(d,0x3f,sizeof(d));
d[s]=0;
priority_queue,greater
>que; //按照first从小到大顺序取出
que.push(P(0,s));
while(!que.empty()){
P p=que.top();que.pop();
int v=p.second;
if(p.first>d[v])continue;
for(int i=0;id[v]+e.cost){
d[e.to]=d[v]+e.cost;
que.push(P(d[e.to],e.to));
}
}
}
}
3、单源最短路(SPFA算法)
描述:
队列优化后的Bellman-Ford算法,减少了冗余的松弛操作。优化:在Bellman-Ford算法中,要是某个点的最短路径估计值更新了,那么我们必须对所有边指向的终点再做一次松弛操作;在SPFA算法中,某个点的最短路径估计值更新,只有以该点为起点的边指向的终点需要再做一次松弛操作。
复杂度:
O(kE) 不稳定,最坏<O(VE)
// SPFA算法
struct edge{
int to,cost;
edge(int t,int c):to(t),cost(c){};
};
int V,E;
bool vis[Max_v]; //在队列标志
int d[Max_v];
int cnt[Max_v]; //入队次数
vectorG[Max_v];
bool SPFA(int s){
memset(vis,0,sizeof(vis));
memset(d,0x3f,sizeof(d));
memset(cnt,0,sizeof(cnt));
vis[s]=1;d[s]=0;cnt[s]=1;
queueque;
que.push(s);
while(!que.empty()){
int u=que.front();que.pop();
vis[u]=0;
for(int i=0;id[u]+G[u][i].cost){
d[v]=d[u]+G[u][i].cost;
if(!vis[v]){
vis[v]=1;que.push(v);
if(++cnt[v]>V)return false; //判定负环
}
}
}
}
return true;
}
4、多源最短路(Floyd算法)
描述:
运用动态规划思想的一个经典多源最短路算法。可以处理负权图的最短路和传递闭包,而判断图中是否有负圈,只需要检查是否存在d[i][i]是负数的顶点i就可以了。递推方程:d[i][j]=min(d[i][j],d[i][k]+d[k][j])
复杂度:
O(v3)
// Floyd算法
int n,m;
int d[Max_n][Max_n];
int path[Max_n][Max_n]; //记录路径
void init(){ //初始化
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
d[i][j]=inf;
path[i][j]=j; //path[i][j]=x表示i到j的路径上(除i外)的第一个点是x
if(i==j)d[i][j]=0;
}
}
void getpath(int st,int ed){ //打印路径
while(st!=ed) {
printf("%d->",st);
st=path[st][ed];
}
printf("%d\n",ed);
}
void Floyd(){ //Floyd算法
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
if(d[i][j]>d[i][k]+d[k][j]){
d[i][j]=d[i][k]+d[k][j];
path[i][j]=path[i][k];
}
}
}
5、被气死的WA
for(int i=0;i<=N;i++)G[i].clear();
//Vector容器清空while(!que.empty())que.pop();
//Queue容器清空6、Prim算法 -----让一棵小树长大
描述:
又称"加点法",运用贪心思想,从某个顶点出发,不断向生成树顶点集合X添加距离X最近的顶点。添加顶点数 < V时,图不连通。
复杂度:
O(v2)
// Prim算法
// 已求得生成树的顶点集合记为X
int V,E;
int dist[Max_v]; //从集合X出发的边到每个顶点的最小权值
bool vis[Max_v]; //顶点i是否在集合X中
int cost[Max_v][Max_v]; //cost[u][v]表示边e=(u,v)的权值(不存在设为INF)
int Prim()
{
int ans=0,sum=0; //收入顶点数和最小权重和
memset(dist,0x3f,sizeof(dist));
memset(vis,0,sizeof(vis));
dist[0]=0;
while(true)
{
int v=-1;
for(int i=0;icost[v][i])
dist[i]=cost[v][i];
}
if(ans
7、Kruskal算法 -----将森林合并成树
描述:
又称"加边法",运用贪心思想,按边的权值从小到大排序,依次从边中选择一条不会产生环路的具有最小权值的边加入生成树的边集合中。添加边数 < V-1时,图不连通。
复杂度:
O(ElogE)
// Kruskal算法
struct edge{
int from,to,cost;
bool operator<(const edge& e)const{
return cost
8、被气死的WA
cost[a-1][b-1]=c;cost[b-1][a-1]=c;
e[i].from=a-1;e[i].to=b-1;e[i].cost=c;
for(int i=0;i时,多组数据会WA!
66666666666666666666666666