无向图最小生成树、次小生成树,最短路模板
2014-03-09 10:47
465 查看
最小生成树有两种模板,都比较简单。
prime算法:
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;
}
最小生成树有两种模板,都比较简单。
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;
}
相关文章推荐
- 无向图最小生成树、次小生成树,最短路模板
- 无向图最小生成树、次小生成树,最短路模板
- 【模板】最短路&&最小生成树
- 最短路,最小生成树,及拓扑排序模板整理
- 最小生成树&最短路模板
- 51nod 1212 无向图最小生成树(prim,kruskal模板)
- 常见模板(欧拉筛素数,最小生成树,快排,并查集,单源最短路)
- 最小生成树(prim算法与kruskal算法)(模板)
- hdoj 4786 Fibonacci Tree 【最小生成树&&最大生成树】
- 最小生成树 最短路 并查集
- 图的生成树和最小生成树
- 贪心算法_无向图最小生成树*
- 51Nod 1212 无向图最小生成树
- [洛谷3366]【模板】最小生成树
- A - 无向图最小生成树
- [曼哈顿距离最小生成树 模板题] POJ 3241 Object Clustering
- 最大生成树与最小生成树
- Civil and Evil Engineer(最小生成树+最大生成树)
- NOIP2017 7.17模拟 Minimum (最短路+最小生成树)
- 最小生成树C++模板