prim算法 优化前O(n²) 优化后O(n-k)

1.算法思想
图采用邻接矩阵存储,贪心找到目前情况下能连上的权值最小的边的另一端点,加入之,直到所有的顶点加入完毕。
2.算法实现步骤
设图G=(V,E),其生成树的顶点集合为U。
(1)把v0放入U。
(2)在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。
(3)把(2)找到的边的v加入U集合。如果U集合已有n个元素,则结束,否则继续执行(2)。
最后得到最小生成树U=

#include<cstdio>
#include<cstring>
using namespace std;
#define vmax 200
int w[vmax][vmax],i,j,k,v,e;

void prim(int v0)
{
    bool flag[vmax];
    int min,nextk,prevk;
    memset(flag,false,sizeof(flag));
    flag[v0]=true;
    for (i=1;i<=v-1;i++)
      {
        min=0x7fffffff;
        for (k=1;k<=v;k++)
          if (flag[k])
            for (j=1;j<=v;j++)
              if (!flag[j] && w[k][j]<min && w[k][j]!=0)
                {
                  min=w[k][j];
                  nextk=j;
                  prevk=k;
                }
        if (min!=0x7fffffff)
          {
            flag[nextk]=true;
            printf("%d %d %d",prevk,nextk,min);
          }
      }
}

int main()
{
    memset(w,0,sizeof(w));
    scanf("%d %d",&v,&e);
    for (k=1;k<=e;k++)
      {
        scanf("%d %d",&i,&j);
        scanf("%d",&w[i][j]);
        w[j][i]=w[i][j];
      }
    prim(1);
    return 0;
}

3.算法的关键与优化
我们很容易就可以发现prim算法的关键:每次如何从生成树T到T外的所有边中,找出一条最小边。例如,在第k次前,生成树T中已有k个顶点和(k-1)条边,此时,T到T外的所有边数为k*(n-k),当然,包括没有边的两顶点我们记权值为“无穷大”的边在内,从如此多的边中查找最短边,时间复杂度为O(k(n-k)),显然无法满足我们的期望。
我们来看O(n-k)的方法:假定在进行第k次前已经保留着从T中到T外的每一个顶点(共n-k个)的各一条最短边,在进行第k次时,首先从这(n-k)条最短边中,找出一条最最短边(它就是从T到T外的最短边),假设为(vi,vj),此步需要进行(n-k)次比较;然后把边(vi,vj)和顶点vj并入T中的边集TE和顶点集U中,此时,T外只有n-(k+1)个顶点,对于其中的每个顶点vt,若(vj,vt)边上的权值小于原来保存的从T中到vt的最短边的权值,则用(v,vt)修改之,否则,保持原最小边不变。这样就把第k次后T中到T外的每一个顶点vt的各一条最短边都保留下来了,为第(k+1)次做好了准备。这样,prim的总时间复杂度为O(n²)。
【样例输入】
6 10
1 2 10
1 5 19
1 6 21
2 3 5
2 4 6
2 6 11
3 4 6
4 5 18
4 6 14
5 6 33
【样例输出】
50
优化后:

#include<cstdio>
using namespace std;
#define MXN 1000
int map[MXN][MXN],cost[MXN],visit[MXN],i,j,n,m,x,y,v;

int prim()
{
    int i,j,min,mini,ans;
    ans=0;
    for (i=1;i<=n;i++)
      {
        visit[i]=false;
        cost[i]=0x7fffffff;
      }
    for (i=2;i<=n;i++)
      if (map[1][i]!=0)
        cost[i]=map[1][i];
    visit[1]=true;
    for (i=1;i<=n-1;i++)
      {
        min=0x7fffffff;
        for (j=1;j<=n;j++)
          if (!visit[j] && cost[j]<min)
            {
              min=cost[j];
              mini=j;
            }
        visit[mini]=true;
        ans+=min;
        for (j=1;j<=n;j++) 
          if (!visit[j] && map[mini][j]>0 && map[mini][j]<cost[j])
            cost[j]=map[mini][j];
      }
    return ans;
}

int main()
{
    scanf("%d %d",&n,&m);
    for (i=1;i<=m;i++)
      {
        scanf("%d %d %d",&x,&y,&v);
        if (map[x][y]==0 || map[x][y]>v)
          {
            map[x][y]=v;
            map[y][x]=v;
          }
      }
    printf("%d",prim());
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值