图论-Kurskal,Dijkstra,spfa,最小生成树整理

Kurskal算法-

Kurskal算法的核心是排序与利用并查集来维护一个联通的树。

我们要利用并查集的find函数来查找两个点的祖先是否为同一个点。

因为算法在进行并查集维护的时候是按边权从小到大来便利的,所以,相连的树很有可能是两个互不相干的集合。

判断两个点的祖先是否是同一个,如果是同一个的话,那么说明他们两个已经相联通则不进行操作。

//边结构体
struct
Edge { int u,v,w;//点u与点v相连,权值为w。 }

如果两个点的祖先不同,则说明这两个点不相互联通,把其中一个点的祖先改变为另一个点的祖先。

当所联通的结点达到了总结点数的时候,退出操作。

1 int find(int x)//find函数
2 {
3       if(fa[x]==x) return x;
4       return fa[x]=find(fa[x]);
5 }    

如此,Kurskal算法就完成了。

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 #define maxn 10000
 5 int fa[maxn],n,ans;
 6 int find(int x)
 7 {
 8     if(fa[x] == x) return x;
 9     return fa[x]=find(fa[x]);
10 }
11 struct edge
12 {
13     int u,v,w;
14 }Edge[maxn];
15 bool cmp(edge a,edge b)
16 {
17     return a.w<b.w;
18 }
19 int main()
20 {
21     scanf("%d",&n);
22     for(int i=1;i<=n;i++)
23     {
24         int x,y,w;
25         scanf("%d%d%d",&x,&y,&w);
26         Edge[i].u=x;
27         Edge[i].v=y;
28         Edge[i].w=w;
29         fa[x]=x;fa[y]=y;
30     }
31     sort(Edge+1,Edge+n+1,cmp);
32     for(int i=1;i<=n;i++)
33     {
34         int u=Edge[i].u,v=Edge[i].v,w=Edge[i].w;
35         if(find(u)==find(v))  continue;
36         fa[find(u)]=v;
37         ans+=w;
38         printf("u=%d v=%d\n",u,v);
39     }
40     printf("%d",ans);
41     return 0;
42 }
View Code

 

Dijkstra算法-

有两个集合一个为s表示已经求出其最短路径点的集合,另一个集合为u表示没有求出其最短路径点的集合

可以理解为贪心。

开始的时候把起点丢进s集合中,搜索和u相连的所有边,找到其中在u集合中最短的点相连的边,把这个点当为中转点

判断dis[u]+g[u][v]是否小于dis[v]如果小于,就把dis[v](从起点到v点的最小值)替换为dis[u]+g[u][v]。

重复上述步骤,直到所有点都进入到s集合中结束。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define Maxn 1000
 6 int g[Maxn][Maxn];//邻接矩阵储存图
 7 int tmp,dis[Maxn];//tmp记录有多少个点,dis表示起点f到e点的最小值。
 8 bool vis[Maxn];//表示每个点是否已经走过
 9 int dijkstar(int f,int e)//起点f,终点e
10 {
11     for(int i=1;i<=tmp;i++)//初始化dis
12     {
13         dis[i]=g[f][i];
14         vis[i]=0;
15     }
16     dis[f]=0;
17     vis[f]=1;//标记f点
18     for(int i=1;i<=tmp;i++)
19     {
20         int Min=2147483647,p=0;
21         for(int j=1;j<=tmp;j++)
22             if(!vis[j]&&dis[j]<Min)
23                 Min=dis[j],p=j;
24         vis[p]=1;
25         for(int j=1;j<=tmp;j++)
26             if(!vis[j]&&dis[j]>dis[p]+g[p][j])
27                 dis[j]=dis[p]+g[p][j];
28     }
29     return dis[e];
30 }
31 int main()
32 {
33     int n;
34     scanf("%d",&n);
35     memset(g,0x3f,sizeof g);
36     for(int i=1;i<=n;i++)
37     {
38         int x,y,w;
39         scanf("%d%d%d",&x,&y,&w);
40         g[x][y]=g[y][x]=w;
41         tmp=max(x,max(tmp,y));        
42     }
43     int ans=dijkstar(fitst,end);//first为起点,end为终点
44     printf("%d",ans);
45     return 0;
46 }
View Code

 

 

Spfa算法-

感觉核心就是搜索,在这里给出BFS版本的Spfa。

利用链式前向星来遍历,这里给出代码。

 1 struct Edge
 2 {
 3     int next,to,w;
 4 }edge[Maxn];
 5 void add(int u,int v,int w)
 6 {
 7     edge[cnt].w=w;
 8     edge[cnt].to=v;
 9     edge[cnt].next=head[u];
10     head[u]=cnt++;
11 }

我们用一个dis数组来表示从起点u到v点的最短路径,一个bool型vis数组来表示这个点是否已经入队。

所以我们先把dis数组全部初始为极大值。

一个队列q,把起点u入队,dis[u]初始为0,vis[u]=true,u入队。

开始搜索。

让队头值出队,再搜索与队头点相连的所有点,如果dis[队头点]+所相连点 边的权值小于dis[相连点] 的话,更新dis[相连点]=其两点间边的权值。同时判断这个点有没有在队列中,如果没有,则入队。

直到队列中没有任何数后,退出。

这样的话,我们就可以找到从起点u到任意一点v的最短路径值了。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <queue>    
 4 #include <algorithm>
 5 using namespace std;
 6 #define Maxn 1000
 7 int head[Maxn],cnt=1,n,tmp;
 8 int vis[Maxn],dis[Maxn];
 9 struct Edge
10 {
11     int to,next,w;
12 } edge[Maxn];
13 
14 void add(int u,int v,int w)
15 {
16     edge[cnt].to=v;
17     edge[cnt].w=w;
18     edge[cnt].next=head[u];
19     head[u]=cnt++;
20 }
21 void spfa(int u)
22 {
23     queue<int> q;
24     memset(dis,0x3f,sizeof dis);
25     dis[u]=0;
26     vis[u]=1;
27     q.push(u);
28     while(!q.empty())
29     {
30         int t=q.front();
31         q.pop();
32         vis[t]=0;
33         for(int i=head[t];i;i=edge[i].next)
34         {
35             int tmp=edge[i].to;
36             if(dis[tmp]>dis[t]+edge[i].w)
37             {
38                 dis[tmp]=dis[t]+edge[i].w;
39                 if(!vis[tmp])
40                 {
41                     q.push(tmp);
42                     vis[tmp]=1;
43                 }
44             }
45 
46         }
47     }
48 }
49 int main()
50 {
51     scanf("%d",&n);
52     for(int i=1;i<=n;i++)
53     {
54         int x,y,z;
55         scanf("%d%d%d",&x,&y,&z);
56         add(x,y,z);
57         add(y,x,z);
58         tmp=max(x,max(tmp,y));
59     }
60     spfa(1);
61         printf("%d",dis[7]);
62     return 0;
63 }
View Code

 

 

Floyd算法-

假如有3个点 x, x1 x2.

x与x2相连边权为a,x与x1相连边权为b,x1与x2相连边权为c。

这时我们比较a是否大于b+c。

如果a>b+c则说明比起从x点直接到x2点来说,从x点先到x1点再周转到x2点所走的距离更短。

如此我们可以遍历每一个点和边利用一个中专值k来表示上述的过程。

用邻接矩阵来储存的话,最终u点到v点的最短路径就是f[u][v](f数组储存图)

核心代码:(大三层循环)

1 for(int k=1;k<=n;k++)
2     for(int i=1;i<=n;i++)
3         for(int j=1;j<=n;j++)
4             f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

 

 

posted @ 2017-04-12 21:23  lxzyzby  阅读(328)  评论(0)    收藏  举报