您的位置:首页 > 移动开发 > IOS开发

无向图最小生成树、次小生成树,最短路模板

2014-03-09 10:47 465 查看

最小生成树有两种模板,都比较简单。

prime算法:

#include<stdio.h>
#include<string.h>
#define MAX 99999999

int g[110][110];

int n;
int prim()
{
int low[110];
int flag[110];
memset(flag,0,sizeof(flag));
for(int i=1; i<=n; i++)
{
low[i]=g[1][i];
}
int count=0;
flag[1]=1;
for(int i=1; i<n; i++)
{
int min=MAX+1;
int k;
for(int j=1; j<=n; j++)
{
if(flag[j]==0&&min>low[j])
{
min=low[j];
k=j;
}
}
flag[k]=1;
count+=low[k];
for(int j=1; j<=n; j++)
{
if(flag[j]==0&&low[j]>g[j][k])
{
low[j]=g[j][k];
}
}
}
return count;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{

int sum;
sum=prim();
printf("%d\n",sum);
}
return 0;
}


kruskal算法:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAX 6005
using namespace std;
typedef struct
{
int v1,v2,distance;
} Edge;
Edge roads[MAX+1];
int tree[110];
bool cmp(const Edge&a,const Edge&b)
{
return a.distance<b.distance;
}
int Find(int x)
{
while(x!=tree[x])
x=tree[x];
int u=x,v;
while(tree[u]!=x)
{
v=tree[u];
tree[u]=x;
u=v;
}
return x;
}
void Union(int x,int y)
{
int fx=Find(x),fy=Find(y);
if(fx!=fy)
tree[fx]=fy;
}

int main()
{
int n,q,a,b,i,j,k,tmp,sum;
while((scanf("%d",&n))!=EOF)
{

for(i=0; i<110; ++i)
{
tree[i]=i;
}
sort(roads,roads+k,cmp);
for(i=0,sum=0; i<k; ++i)
{
if(Find(roads[i].v1)!=Find(roads[i].v2))
{
Union(roads[i].v1,roads[i].v2);
sum+=roads[i].distance;
}
}
printf("%d\n",sum);
}
return 0;
}


以下是次小生成树思想:

 邻集的概念:由T进行一次可行交换得到的新的生成树所组成的集合,称为树T的邻集,记为N(T);

 设T是图G的最小生成树,如果T1满足ω(T1)=min{ω(T’)|T’∈N(T)},则T1是G的次小生成树;

 首先先求该图的最小生成树T,时间复杂度O(Vlog2V+E);

 然后,求T的邻集中权值和最小的生成树,即图G 的次小生成树;

 如果只是简单的枚举,复杂度很高;

 首先枚举两条边的复杂度是O(VE),再判断该交换是否可行的复杂度是O(V),则总的时间复杂度是O(V2E);

 分析可知,每加入一条不在树上的边,总能形成一个环,只有删去环上的一条边,才能保证交换后仍然是生成树;

 而删去边的权值越大,新得到的生成树的权值和越小,可以以此将复杂度降为O(VE);

 更好的方法:首先做一步预处理,求出树上每两个结点之间的路径上的权值最大的边;

 然后枚举图中不在树上的边,有了预处理,就可以用O(1)的时间得到形成的环上的权值最大的边;

 预处理:因为是一棵树,只要简单的BFS即可,预处理所要的时间复杂度为O(V2);

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 107
#define inf 0x3f3f3f
using namespace std;
int g[M][M],path[M][M];//path求的是i到j最大的边权
int dist[M],pre[M],vis[M];
bool used[M][M];//是否在最小生成树中
int n,m,mst;

void init()
{
for(int i=0;i<=n;i++)
for(int j=i+1;j<=n;j++)
g[i][j]=g[j][i]=inf;

}

int prime()
{
int mst=0;
memset(path,0,sizeof(path));
memset(vis,0,sizeof(vis));
memset(used,0,sizeof(used));
vis[1]=1;
for(int i=1;i<=n;i++)
{
dist[i]=g[1][i];
pre[i]=1;
}
for(int i=1;i<n;i++)
{
int u=-1;
for(int j=1;j<=n;j++)
{
if(!vis[j])
if(u==-1||dist[j]<dist[u])
u=j;
}
used[u][pre[u]]=used[pre[u]][u]=true;//加入mst
mst+=g[pre[u]][u];
vis[u]=1;
for(int j=1;j<=n;j++)
{
if(vis[j]&&j!=u)//从u到j这条路径上最大边的权值
path[j][u]=path[u][j]=max(path[j][pre[u]],dist[u]);
if(!vis[j])
if(dist[j]>g[u][j])//更新相邻节点的距离
{
dist[j]=g[u][j];
pre[j]=u;//记录他的前驱
}
}
}
return mst;
}

int second_tree()//求次小生成树
{
int res=inf;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&!used[i][j])
res=min(res,mst-path[i][j]+g[i][j]);//删除树上权值最大的路径并且加上这条路径其它边
return res;
}

int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();

mst=prime();//最小生成树
int second_mst=second_tree();//次小生成树
}
}

最短路也有两种简单的算法。

Dijkstra算法:

#include<iostream>
#include<stdio.h>
#include<iomanip>
using namespace std;
#define N 10000
#define MAX 100000099
int a

;
int dist
;

void dijkstra(int n)
{
int s
,newdist;
for(int i=1; i<=n; i++)
{
dist[i]=a[1][i];
s[i]=0;
}
dist[1]=0;
s[1]=1;
for(int i=2; i<=n; i++)
{
int j,tem=MAX;
int u=1;
for(j=2; j<=n; j++)
if(!s[j]&&dist[j]<tem)
{
u=j;
tem=dist[j];
}
s[u]=1;
for(j=2; j<=n; j++)
{
if(!s[j]&&a[u][j]<MAX)
{
newdist=dist[u]+a[u][j];
if(newdist<dist[j])
dist[j]=newdist;

}
}
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m),m||n)
{
for( i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
a[i][j]=MAX;
dist[i]=MAX;
}

dijkstra(n);
cout<<dist
<<endl;
}
return 0;
}


Floyd算法:

#include<stdio.h>
int map[105][105];
int main()
{
int n,m,i,j,k,a,b,c;
while(scanf("%d%d",&n,&m),n|m)
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
map[i][j]=10000000;

for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(map[i][k]+map[k][j]<map[i][j])
map[i][j]=map[i][k]+map[k][j];
printf("%d\n",map[1]
);

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息