最优布线问题

 TZOJ   5788:  最优布线问题

 

描述

学校有n台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们有数据线连接。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。

当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。为了节省费用,我们采用数据的间接传输手段,即一台计算机可以间接的通过若干台计算机(作为中转)来实现与另一台计算机的连接。

现在由你负责连接这些计算机,任务是使任意两台计算机都连通(不管是直接的或间接的)。

输入

第一行为整数n(2≤n≤100),表示计算机的数目。此后的n行,每行n个整数。第x+1行y列的整数表示直接连接第x台计算机和第y台计算机的费用。

输出

一个整数,表示最小的连接费用。

样例输入

3
0 1 2
1 0 1
2 1 0

样例输出

2

提示

注:表示连接1和2,2和3,费用为2。

 

题目大意,找出一条能链接所有电脑的 小网线 长度和。(知识点: 最小生成树)

解题方法有两种:

1、普里姆算法 ( prim )(适用于稠密矩阵情况)

2、克鲁斯卡尔( kreskal )(适用于稀疏矩阵情况)

本题 本人代码 采用    克鲁斯卡尔( kreskal ) 方法,  因为写着方便,思路简单

总过程:

1.先把所有边以 边权值 大小 从小到大 排序;

2.从最小的边开始遍历排序后的所有边,若该边的端点没有在同个集合里,然后把两个集合合并,并将权值加入输出结果里,若在同个集合里,则跳过,进行下一条边的比较;

3.输出结果

 

思路

假设所有电脑都两两任意连接,那么整个图就有很多的回路,为求得 最短 能连接所有电脑的 网线长度,可以在保证每一个电脑还连在图的前提下,逐一去掉的最长的网线,不影响图的连通;

即最后剩下的网线为最短的网线连接方式

 

本题采用的  方法 类似,但是是反过来的,即从最小的边开始,假设所有的电脑都没连上,然后从最小的边开始把电脑两两连接;

 

(大概思路是这样的,想证明该算法的正确性可以看看这个 [最小生成树]Kruskal算法及正确性证明 

 

代码:

#include <bits/stdc++.h>
using namespace std;
class node
{
    public:
    int x,y,quan;//x为开始的点,y是结束的点,quan是边的权值
}num[10100];
int q[110]={0};//记录每个点的所在的集合,下标为当前的点,记录的值是该点所在的集合,若记录的值相同 则 属于同一集合
bool cmp(node a,node b)//重写sort,以 边的权 从小到大排序
{
    return a.quan<b.quan;
}
int find(int n)//寻找每个节点的集合
{
    return q[n]==n?n:q[n]=find(q[n]);//如果该点的集合为自己本身,则返回该点的值,否则以该点记录的集合进行递归继续寻找
}
int main()
{
    int n;
    cin>>n;
    int quan,t=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>quan;
            if(quan)//如果没有连接(权值为0)则不输入,不然权值为0的会排在前面,然后被连接上,答案错误
            {
                num[t].x=i;
                num[t].y=j;
                num[t++].quan=quan;//t表示有多少条边
            }
        }
    }
    for(int i=1;i<=n;i++)//标记每个点所在的 初始 集合为自己本身
    {
        q[i]=i;
    }
    sort(num,num+t,cmp);//排序
    int ans=0;
    for(int i=0;i<t;i++)
    {
        if(find(num[i].x)==find(num[i].y))continue;//如果两端点在同一集合,即两端点已经被间接或直接地被连接上了,则跳过,舍弃该边
        ans+=num[i].quan;
        q[find(num[i].y)]=find(num[i].x);//如果这步是把两个集合合并,需要把两个集合合并,即把其中一个点的所在的集合的根节点改成另一个点所在的集合的根节点
                         //这里两个集合谁改成谁的根节点都没影响
                         //连接两个集合时一定是改集合的根节点(即根节点对应的 q[] 的值),不能只改 该循环 加上去的边的端点的 q[] 的值,我当时在这错了没即使注意到。
} cout<<ans<<endl; }

 

posted @ 2022-05-17 22:08  Minza  阅读(149)  评论(0编辑  收藏  举报