输入图的邻接矩阵,求最小生成树的总权值(多组数据)
Input
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.
Output
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.
Sample Input
4 0 4 9 21 4 0 8 17 9 8 0 16 21 17 16 0
Sample Output
28
prim算法:
1.在一个加权连通图中任意选出一个点作为初始顶点,标记为计算所有与之相连接的点的距离,选择距离最短的,加入到建成最小生成树的队伍中来
2. 重复以下操作,直到所有点都加入进来:
需要设置的变量及考虑的问题:
1. 每个点包含两个因素,它的邻接点及它与邻接点的长度,用结构体
2. roads[ ][ ]起初存放两两之间的距离用一个二维数组存放.
3. vused[ ]为防止重复使用一些点和防止出现回路,则需要设置vused标记
4. done:为了判断是否所有点都加入到生成树里,需要记录加入点的个数
5. total:题目求最小生成树的总长度
6.需要每次选出所有未使用点中到正在建成生成树的距离中最短的那个加入到生成树队列中来,可以使用优先队列.
7.vdist[ ]:需要一个变量来存放各个未使用点到正在建成的生成树的距离,每次加入一个点之后都需要更新一下各个点的vdist[],初始化不要忘记!
/*每次选距离生成树最短的点*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
const int inf=1<<30;
using namespace std;
struct road{
int v;
int l;
road(int vv,int ll):v(vv),l(ll){}
bool operator<(const road &r) const{
return l>r.l;//队列里权值越小越优先
}
};
vector< vector<road> > roads(110);
int prim(const vector<vector<road> > &roads,int n){
int i,j,k;
road x(0,0);
priority_queue<road> q;
vector<int> vdist(n);//各顶点到已建好的那部分的距离
vector<int> vused(n);//标记顶点是否被加入最小生成树
int done=0;
for(int i=0;i<n;i++){//初始化
vused[i]=0;
vdist[i]=inf;
}
done=0;
int total=0;
q.push(road(0,0));//填入顶点0
while(done<n&&!q.empty()){
do{
x=q.top();//离正在建的生成树距离最小的点
q.pop();
}while(vused[x.v]==1&&!q.empty());
if(vused[x.v]==0){
total+=x.l;
vused[x.v]=1;
done++;
for(int i=0;i<roads[x.v].size();i++){//与点x.v相通的各个点枚举
int k=roads[x.v][i].v;
if(vused[k]==0){
int l=roads[x.v][i].l;
if(vdist[k]>l){
vdist[k]=l;//每次更新点k到正在建成的最小生成树的最短距离
q.push(road(k,l));//将各个点到最小生成树的距离加入队列,这样就可以每次选出最短距离的那个点了
}
}
}
}
}
if(done<n)
return -1;
return total;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++)
roads[i].clear();
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
int l;
scanf("%d",&l);
roads[i].push_back(road(j,l));
}
}
printf("%d\n",prim(roads,n));
}
}
kruskal算法:
每次选出距离最短的边
需要设置的变量及考虑的问题:
1.边的因素有三个,两个端点,及边的长度,用结构体
2.对边的权值进行排序,这样就可以每次选最短的值就方便了
3.选出边时,为防止选出的边与已选择的边形成回路,需要进行判断,就可以判断该边的两个端点是否都在那个已选择集合中,这样可以用到并查集的算法,每次找点的跟,看是否是一棵树上的。
4. total:题目求最小生成树的总长度
5.done:为了判断是否所有点都加入到生成树里,记录最小生成树的边数。
/*每次选最小的边*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
struct road{
int u;
int v;
int l;
road(int uu,int vv,int ll):u(uu),v(vv),l(ll){}
road(){}
bool operator<(const road &r) const{
return l<r.l;//边从小到大排序
}
};
vector <road> roads;
vector <int> par;
int getroot(int a){
if(par[a]==a){
return a;
}
par[a]=getroot(par[a]);
return par[a];
}
void Merge(int a,int b){
int pa=getroot(a);
int pb=getroot(b);
if(pa==pb)
return;
par[pb]=pa;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
par.clear();//初始化
roads.clear();
for(int i=0;i<n;i++)
par.push_back(i);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
int l;
scanf("%d",&l);
roads.push_back(road(i,j,l));
}
}
sort(roads.begin(),roads.end());
int done=0;//加入的点数
int total=0;//加入之后的总长度
for(int i=0;i<roads.size();i++){
if(getroot(roads[i].u)!=getroot(roads[i].v)){//若这条边的两端点不在同一连通分量上,就可以加入到最小生成树的队伍中,若在同一连通分量上,则再加入这条边会形成回路
Merge(roads[i].u,roads[i].v);
done++;
total+=roads[i].l;
// printf("%d\n",total);
}
if(done==n-1)//说明所有点已加入生成树里了
break;
}
printf("%d\n",total);
}
}