HDU 1102 Constructing Roads

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1102

题目大意:给出一些顶点一些边,给出邻接矩阵,并且某些点已经连通,求最小生成树。

最小生成树算法基于贪心思想。

一开始设顶点集合S={},初始化所有顶点到集合S距离最小值为无穷大,置V1到集合S距离为0

(1)选入到集合S距离最小的顶点Vi

(2)更新未选入到S的顶点到S的最小距离

不断进行步骤(1)(2)直到所有顶点都被选入至集合S中

朴素prim算法O(v^2)

#include<iostream>
using namespace std;
const int maxn=105,inf=1<<30;
int Map[maxn][maxn],vis[maxn],d[maxn];
int n,q,ans;
int prim()
{
    fill(vis,vis+maxn,0);//初始化每个点都未被加入到答案集合中
    fill(d,d+maxn,inf);//初始化每个顶点到答案集合的最近距离
    d[1]=0;//将顶点1加入到答案集合中
    ans=0;//最小生成树权值
    while(true)
    {
        int v=-1;//记录下一个将要加入答案集合的顶点
        for(int i=1;i<=n;i++)//贪心选取离答案集合距离最近的顶点
            if(!vis[i]&&(v==-1||d[i]<d[v])) v=i;
        if(v==-1) break;//如果顶点都访问完了,那么v必然等于-1,则退出循环,算法结束
        vis[v]=1;//加入答案集合
        if(d[v]==inf) return -1;//存在孤立点,则不存在最小生成树
        ans+=d[v];//加上权值
        for(int i=1;i<=n;i++)//更新未加入答案集合的那些顶点到答案集合的最小距离
            if(!vis[i]) d[i]=min(d[i],Map[v][i]);
    }
    return ans;
}
int main()
{
    while(cin>>n)
    {
        fill(&Map[0][0],&Map[maxn][0],inf);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            cin>>Map[i][j],Map[j][i]=Map[i][j];
        cin>>q;
        while(q--)
        {
            int x,y;
            cin>>x>>y;
            Map[x][y]=Map[y][x]=0;
        }
        cout<<prim()<<endl;
    }
    return 0;
}

可以发现,复杂度之所以高是因为,每次要选出离集合距离最近的顶点都要用O(n)来扫一遍选取。如果这个步骤能快一点就好了,基于这个想法我们很快就可以想到,每个顶点到集合的距离可以用优先队列来维护,这样每次选取顶点只要O(logn)的复杂度。

#include<iostream>
#include<queue>
using namespace std;
const int maxn=105,inf=1<<30;
int Map[maxn][maxn],vis[maxn],ans,n,d[maxn];
struct node
{
    int v,len;//v代表当前顶点,w代表v到答案集合的最小距离
    friend bool operator <(node a,node b)
    {
        return a.len>b.len;
    }
};
int prim()
{
    priority_queue<node>q;
    node t;
    t.v=1;t.len=0;
    q.push(t);//初始化顶点1到答案集合的距离为0,以保证第一次一定选入顶点1到答案集合中去
    ans=0;
    fill(vis,vis+maxn,0);
    fill(d,d+maxn,inf);
    while(q.size())
    {
        t=q.top();q.pop();//取离答案集合距离最小的顶点
        if(vis[t.v]) continue;//如果已经在答案集合中则进行下一次的循环
        vis[t.v]=1;//否则取顶点t.v加入到答案集合中
        ans+=t.len;
        for(int i=1;i<=n;i++)//更新未加入答案集合的顶点到答案集合 的距离
        {
            if(!vis[i]&&Map[t.v][i]<d[i])//可以更新则入队
            {
                node next;
                next.v=i;
                next.len=Map[t.v][i];
                d[i]=Map[t.v][i];
                q.push(next);
            }
        }
    }
    return ans;
}
int main()
{
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            cin>>Map[i][j];
        int q;
        cin>>q;
        while(q--)
        {
            int x,y;
            cin>>x>>y;
            Map[x][y]=Map[y][x]=0;//x和y相连,那么它们之间的距离为0
        }
        cout<<prim()<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值