HDU 1102 Constructing Roads, Prim+优先队列

11 篇文章 0 订阅
7 篇文章 0 订阅

题目链接:HDU 1102 Constructing Roads

Constructing Roads



Problem Description
There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected. 

We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.
 

Input
The first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j.

Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.
 

Output
You should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum. 
 

Sample Input
  
  
3 0 990 692 990 0 179 692 179 0 1 1 2
 

Sample Output
  
  
179
 

Source
 

Recommend
Eddy   |   We have carefully selected several similar problems for you:   1142  1598  1116  1269  1596 
 
题意

有N个村子要修道路,给出了修每条道路的原始费用,现在有的道路已经修好了。我们要求的是修剩下的道路的最小费用。

分析

最小生成树算法,因为是邻接矩阵,因而使用Prim方法非常方便求解,对于已经修好的道路,我们可以将他们的费用设置为0,再用Prim算法求解即可得到。

代码

邻接矩阵:
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

#define maxn 110
#define INF 0xffff
int g[maxn][maxn];
int n;

struct node
{
    int v, key;
    friend bool operator<(node a, node b)
    {
        return a.key > b.key;
    }
};

bool visited[maxn];
node vx[maxn];
priority_queue<node> q;
void Prim()
{
    for(int i = 1; i <= n; i++)
    {
        vx[i].v = i;
        vx[i].key = INF;
        visited[i] = false;
    }
    vx[1].key = 0;
    q.push(vx[1]);
    while(!q.empty())
    {
        node nd = q.top();
        q.pop();
        int st = nd.v;
        if(visited[st])
            continue;
        visited[st] = true;
        for(int j = 1; j <= n; j++)
        {
            if(j != st && !visited[j] && vx[j].key > g[st][j])
            {
                vx[j].key = g[st][j];
                q.push(vx[j]);
            }
        }
    }
}
int main()
{
    int m, a, b;
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
                scanf("%d", &g[i][j]);
            g[i][i] = INF;
        }
        scanf("%d", &m);
        while(m--)
        {
            scanf("%d%d", &a, &b);
            g[a][b] = g[b][a] = 0;
        }
        Prim();
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += vx[i].key;
        printf("%d\n", ans);

    }
    return 0;
}

邻接表:
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;

#define maxn 110  //最大顶点个数
int n, m;       //顶点数,边数

struct arcnode  //边结点
{
    int vertex;     //与表头结点相邻的顶点编号
    int weight;     //连接两顶点的边的权值
    arcnode * next; //指向下一相邻接点
    arcnode() {}
    arcnode(int v,int w):vertex(v),weight(w),next(NULL) {}
};

struct vernode      //顶点结点,为每一条邻接表的表头结点
{
    int vex;    //当前定点编号
    arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
}Ver[maxn];

void Init()  //建立图的邻接表需要先初始化,建立顶点结点
{
    for(int i = 1; i <= n; i++)
    {
        Ver[i].vex = i;
        Ver[i].firarc = NULL;
    }
}
void Insert(int a, int b, int w)  //尾插法,插入以a为起点,b为终点,权为w的边,效率不如头插,但是可以去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        if(p->vertex == b)
        {
            if(p->weight > w)
                p->weight = w;
            return ;
        }
        while(p->next != NULL)
        {
            if(p->next->vertex == b)
            {
                if(p->next->weight > w);
                    p->next->weight = w;
                return ;
            }
            p = p->next;
        }
        p->next = q;
    }
}
void Insert2(int a, int b, int w)   //头插法,效率更高,但不能去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        q->next = p;
        Ver[a].firarc = q;
    }
}
struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};

#define INF 0xfffff    //权值上限
bool visited[maxn]; //是否已经加入树
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim()    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        visited[i] = false;
    }
    vx[1].key = 0;
    q.push(vx[1]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();
        if(visited[nd.v])   //注意这一句的深意,避免很多不必要的操作
            continue;
        visited[nd.v] = true;
        arcnode * p = Ver[nd.v].firarc;
        while(p != NULL)    //找到所有相邻结点,若未访问,则入队列
        {
            if(!visited[p->vertex] && p->weight < vx[p->vertex].key)
            {
                vx[p->vertex].key = p->weight;
                vx[p->vertex].v = p->vertex;
                q.push(vx[p->vertex]);
            }
            p = p->next;
        }
    }
}

int main()
{
    int m, a, b, x;
    while(~scanf("%d", &n))
    {
        Init();
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                scanf("%d", &x);
                if(x != 0)
                    Insert2(i, j, x);
            }
        }
        scanf("%d", &m);
        while(m--)
        {
            scanf("%d%d", &a, &b);
            Insert(a, b, 0);
            Insert(b, a, 0);
        }
        Prim();
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += vx[i].key;
        printf("%d\n", ans);

    }
    return 0;
}





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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值