最小生成树学习总结

a.问题描述:实际生活中,我们总须找到最优的情况,如修建道路怎样才最省钱。我们可以将实际问题看成一张图,要满足我们的需求,这张图有很多种方案,我们要找出对我们来说最优的一个方案,其他的方案都放弃。那问题就转化成怎样构建一颗最小的的树了。

实现方法有两种:
1.kruskal算法:以边为选择对象,选择过程进行贪心选择,通过并查集实现。适用于稀疏图。
主要步骤:1.将边的信息保存在结构体数组中;
2.按照边权进行排序,小的在前;
3.对边从前向后依次选择,如果此边所关联的两点已经联通则放弃此边,否则选择此边。直到所有边判断完&&选够n-1条边


代码如下:

kruskal:

#include
#include
#include
using namespace std;

const int MAXN=1e6+10;
int fa[MAXN];

struct Edge{
    int from;
    int to;
    int val;
    int flag;
}edge[MAXN];

int findroot(int x)
{
    int r=x;
    while(r!=fa[r])  r=fa[r];
    while(x!=fa[x])
    {
        int tem=fa[x];
        fa[x]=r;
        x=tem;
    }
    return r;
}

bool Union(int x,int y)//对Union进行适当改造 
{
    int fx=findroot(x);
    int fy=findroot(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
        return  true;
    }
    return false;
}

bool cmp(Edge a,Edge b)
{
    return a.valint main()
{
    int sum,n,cnt,i;
    while(~scanf("%d",&n),n)
    {
        for(i=1;i<=n;++i)  fa[i]=i;
        cnt=0;  //记录选了多少条边 
        for(i=1;i<=n*(n-1)/2;++i)
        {
            scanf("%d %d %d %d",&edge[i].from,&edge[i].to,&edge[i].val,&edge[i].flag);
            if(edge[i].flag)  
            {
                Union(edge[i].from,edge[i].to);
                ++cnt;
            }
        }
        sort(edge+1,edge+n*(n-1)/2+1,cmp);
        sum=0;
        for(i=1;i<=n*(n-1)/2&&cnt1;++i)//只写i
        {
            if(Union(edge[i].from,edge[i].to))  
            {
                sum+=edge[i].val; 
                ++cnt;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

2.prime算法:以点为研究对象,将点分成两个集合:一个已经确定是最短路,一个还未确定;用最短路的思路选出n个点。适用于稠密图。
主要步骤:
1.将图保存在临接矩阵之中(便于访问是否邻接);
2.任选一点作为起始点;
3.将此点加入到已确定点集之中;
4.依次点为原点,更新与之相关联的节点到点集的距离(最小);
5. 选中剩余点中距离点集最近的点,加入点集,并以其为原点更新和他相邻接的点的距离………
6. 循环,直到点集中有n个点为止。


代码如下:

prime:

#include
#include
using namespace std;

#define inf 0x3f3f3f  //只能这样定义了 换一种就错 或者下方这种 
//#define INF 0x3f3f3f3f
const int MAXN=1e3+10;
int map[MAXN][MAXN],dis[MAXN];//保存图(顶点邻接矩阵)和点的距离 
bool vis[MAXN];//标记是否访问过 
int ans;//保存答案 

void prime(int n)
{
    memset(vis,false,sizeof(vis)); memset(dis,inf,sizeof(dis));
    ans=0; dis[1]=0;//初始化 任选一点作为原点 
    for(int i=1;i<=n;++i)//依次加入n个点 原点也算 
    {
        int top=inf,k;//记录距已确定集合最近的距离和这一点的下标 
        for(int j=1;j<=n;++j)//查找与已确定集合最近的点 
        {
            if(!vis[j]&&dis[j]//此点没有加入已选集合且距离集合的距离最近 
            {
                top=dis[j];
                k=j;
            }
        }
        vis[k]=true; ans+=top;//将此点加入集合并更新结果 
        for(int j=1;j<=n;++j)//更新与已选点邻接节点的距离 
        {
            if(!vis[j]&&map[k][j]//如果此点没有加入集合且距离集合的距离有更新 
            {
                dis[j]=map[k][j];
            }
        }
    }
}

int main()
{
    int i,n,u,v,w;
    while(~scanf("%d",&n),n)
    {
        memset(map,inf,sizeof(map));//初始化所有点都距离无穷远 
        for(i=1;i<=n*(n-1)/2;++i)
        {
            scanf("%d %d %d",&u,&v,&w);
            map[u][v]=map[v][u]=w;//两点邻接 
        }
        prime(n);
        printf("%d\n",ans);
    }
    return 0;
}

详细讲解:
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html

你可能感兴趣的:(13-最小生成树)