【9004】&&【7010】最优布线问题

来源:互联网 发布:日本阶级固化 知乎 编辑:程序博客网 时间:2024/06/08 18:53

Time Limit: 1 second
Memory Limit: 256 MB

问题描述
学校有n台计算机,为了方便数据传输,现要将他们用数据线连接起来。两台计算机被连接起来是指他们之间有数据线相连。由于计
算机所处的位置不同,因此不同的两台计算机的连接费用往往不同。
当然,如果将任意两条计算机都用数据线连接起来,费用将是相当庞大的。为了节省费用,我们采用数据间接传输手段,机一台计算
机可以间接的通过若干台计算机(作为中转)来实现与另一台计算机的相连。
现在由你负责连接这些计算机,你的任务是是任意两台计算机都联通(不管是间接的或是直接的)。  

Input

输入文件名为wire.in,第一行为整数n(2<=n<=100),表示计算机数目。此后的n行,每行n个整数。第x+1行y列的整数表示直接连
接第x台计算机和第y台计算机的费用。

Output

输出文件名为wire.out,一个整数,表示最小的连接费用。
(注:表示连1和2,2和3,费用为2) 

Sample Input

30 1 21 0 12 1 0

Sample Output

2
【题解】
这题是用来练最小生成树的做法的。
我写了注释。算法的实现原理,可以去找书自己看。
【代码】
法一,克鲁斯卡尔算法 
#include <cstdio>#include <algorithm>using namespace std;struct edge{int fr,to,w;};edge a[10000];int n,su,num,f[101],nn = 0;void input_data(){scanf("%d",&n);for (int i = 1;i <= n;i++) //这是并查集的初始化 每个节点指向它本身。 f[i] = i;for (int i = 1;i <= n;i++)for (int j = 1;j <= n;j++) //读入一个n*n的矩阵。但是有一半是没有用的,是无向图。 {int x;scanf("%d",&x);if (j > i) //只要读取j > i的那一部分矩阵就可以了 {num++;a[num].fr = i; //记录下这条边的信息。 a[num].to = j;a[num].w = x;}}}int cmp(const edge & a,const edge &b) //这是对比函数 return 1表示要按照这样顺序排 {if (a.w < b.w) return 1;return 0;}int findfather(int x) //找根节点 {if (f[x] != x)f[x] = findfather(f[x]);return f[x];}void get_ans() //来累加边 {for (int i = 1;i <= num;i++) //对num条边都进行试探 {int x = a[i].fr,y = a[i].to;int r1 = findfather(x),r2 = findfather(y);if (r1 != r2) //如果这条边的两个点连接着不同的区域 {nn++;f[r2] = r1; //则合并起来 su += a[i].w;if (nn == n-1) //只要有N-1条边就可以宣告结束了。 return;}}}void output_ans(){printf("%d",su);}int main(){//freopen("F:\\rush.txt","r",stdin);input_data();sort(a+1,a+1+num,cmp); //从1开始,下标为num+1结束。 get_ans();output_ans();return 0;}


法二,普里姆算法
#include <cstdio>#include <cstring>int next[10000],first[101],to[10000],num = 0,su,n,w[10000],dis[101],mi;bool bo[101];void input_data(){scanf("%d",&n);for (int i = 1;i <= n;i++)for (int j = 1; j<= n;j++){int x;scanf("%d",&x);if (i!=j) //可以写成 j > i,因为有一半的信息无用 {num++;next[num] = first[i]; //用邻接表的方式存储边的信息 to[num] = j;w[num] = x;first[i] = num;}}}void get_ans(){memset(bo,false,sizeof(bo)); //一开始所有的数字都未加入MST for (int i = 1;i <= n;i++) //扩展i节点所需要的费用都设为正无穷 dis[i] = 2100000000 / 3;dis[1] = 0; //从任意一个点开始进行MST的构造 for (int i = 1;i <= n+1;i++) //n+1随便写的。因为循环内有终止的方法 {mi = 2100000000 / 3;//这是为了获取dis中的最小值 int k = -1;for (int j = 1;j <= n;j++) //找一遍最小值 if (dis[j] < mi && !bo[j]) //一定要没有加入MST的点 {mi = dis[j];k = j;}if (k == -1) //如果没找到就结束 break;bo[k] = true; //记录这个点加入MST su += dis[k]; //累加和 int temp = first[k]; //查看这个点的出度 while (temp!=0){int t = to[temp];if (dis[t] > w[temp]) //尝试更新扩展到t节点所需要的最小花费 dis[t] = w[temp];temp = next[temp];}}}void output_ans(){printf("%d",su);}int main(){//freopen("F:\\rush.txt","r",stdin);input_data();get_ans();output_ans();return 0;}


                                             
0 0
原创粉丝点击