北京大学Openjudge1258:Agri-Net(最小生成树 Prim+优先队列)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37618760/article/details/82024581

1258:Agri-Net

总时间限制: 

1000ms

内存限制: 

65536kB

描述

Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet connectivity to all farms in the area. He needs your help, of course. 
Farmer John ordered a high speed connection for his farm and is going to share his connectivity with the other farmers. To minimize cost, he wants to lay the minimum amount of optical fiber to connect his farm to all the other farms. 
Given a list of how much fiber it takes to connect each pair of farms, you must find the minimum amount of fiber needed to connect them all together. Each farm must connect to some other farm such that a packet can flow from any one farm to any other farm. 
The distance between any two farms will not exceed 100,000. 

输入

The input includes several cases. For each case, the first line contains the number of farms, N (3 <= N <= 100). The following lines contain the N x N conectivity matrix, where each element shows the distance from on farm to another. Logically, they are N lines of N space-separated integers. Physically, they are limited in length to 80 characters, so some lines continue onto others. Of course, the diagonal will be 0, since the distance from farm i to itself is not interesting for this problem.

输出

For each case, output a single integer length that is the sum of the minimum length of fiber required to connect the entire set of farms.

样例输入

4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0

样例输出

28

来源

USACO 102

Source File:

先介绍第一种写法,是o(N^2)的写法

#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 1000001
int dist[101][101];
int n;
int prim(void){
    int s=1;
    int m=1;
    bool visited[101]={false};
    visited[s]=true;

    int min_w;
    int prim_w=0;
    int point;
    int low_dis[100];

    for(int i=1;i<=n;i++)
        low_dis[i]=MAXN;
    while(true){
        if(m==n) break;
        min_w=MAXN;
        for(int i=2;i<=n;i++){

            if(!visited[i]&&low_dis[i]>dist[s][i])
                low_dis[i]=dist[s][i];
            if(!visited[i]&&min_w>low_dis[i]){
                min_w=low_dis[i];
                point=i;
            }
        }
        s=point;
        visited[s]=true;
        prim_w+=min_w;
        m++;
    }
    return prim_w;
}
int main(){
    while(cin>>n){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                cin>>dist[i][j];
            }
        }
        cout<<prim()<<endl;
    }
    return 0;
}

这一种写法其实就是用二维数组dist存储输入的数据,用visited数组表示是否访问过,并且用dis存储每次取到的最小的距离。并且用s,point表示下标,如果访问过,对应的visited数组那一项就变成true。感觉这种方法考虑的比较简单。

第二种算法,是Prim算法和优先队列结合的方法

#include <iostream>
#include<vector>
#include<queue>
#define MAXN 1<<30
using namespace std;
struct Edge{
    int v;//边端点
    int w;//边权值
    Edge(int _v=0,int _w=MAXN):v(_v),w(_w){}
    bool operator <(const Edge &e) const{
        return w>e.w;
    }

};
vector < vector <Edge> > G(110);
int Prim( const vector < vector <Edge> >&G,int n){
    Edge xDist(0,0);
    priority_queue<Edge> pq;
    vector<int> vDist(n);//存放顶点及其在建生成树的距离
    vector<int> vUsed(n);//标记是否访问过
    int nDoneNum=0;
    for(int i=0;i<n;i++){
        vUsed[i]=0;
        vDist[i]=MAXN;
    }
    nDoneNum=0;
    int nTotalW=0;//记录总权值
    pq.push(Edge(0,0));//开始只有顶点0,先储存上
    while(nDoneNum<n&&!pq.empty()){
        do{
            xDist=pq.top();//每次从队列中取出离在建生成树最近的点
            pq.pop();
        }while(vUsed[xDist.v]==1&&!pq.empty());
        if(vUsed[xDist.v]==0){
            //如果有未访问过的情况
            nTotalW+=xDist.w;
            vUsed[xDist.v]=1;
            nDoneNum++;
            for(int i=0;i<G[xDist.v].size();i++){//更新加入点的邻点
                int k=G[xDist.v][i].v;
                if(vUsed[k]==0){
                    int w=G[xDist.v][i].w;
                    if(vDist[k]>w){
                        vDist[k]=w;//更新点k到在建生成树的最小距离
                        pq.push(Edge(k,w));//将距离压入队列
                    }
                }
            }
        }
    }
    if(nDoneNum<n)//图不连通,如果还有其他的点就无法添加进去
        return -1;
    return nTotalW;
}
int main()
{
    int n;
    while(cin>>n){
        for(int i=0;i<n;i++)
            G[i].clear();
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++){
            int w;
            cin>>w;
            G[i].push_back(Edge(j,w));
        }
        cout<<Prim(G,n)<<endl;
    }
}

 对于Prim算法,需要把握以下几点:

图节点数目为N,正在构造的生成树为T,  维护Dist数组,Dist[i]表示ViT的“距离”  开始所有Dist[i] = 无穷大, T 为空集

猜你喜欢

转载自blog.csdn.net/qq_37618760/article/details/82024581