hdu1102最小生成树

1.使用kruskal算法

        题目中给出了一些道路,这些道路将村庄构成了部分连通集。我们要求的是使得整个村庄连通花费最小的道路长度和。实际上就是一个最小生成树问题.。在这里我们已知了给出的一些边(可能不是整个图最小生成树的边,不过这不要紧,只需要接下来我们选择的边长度和最小即可)。在给出的边的集合已经构成了部分连通集,接下来我们将所有的边放在一个最小堆中,每次取出权重最小的边,看边的两个端点是否属于同一个集合,不属于就加上这条边,否则就丢弃,这种贪心选择的思想就是Kruskal算法。在提交代码是出现了几个小问题。struct里面对<的重载必须设定为常量函数,用const限定函数,否则编译不过(以后记住了)。在用堆进行函数传参时一定要用堆的引用,否则传递进去的只是形参,而函数执行完后堆里面数据还存在(就在这里Wa了好多次)。写代码传递参数尽量用引用。

#include<iostream>
#include<fstream>
#include<queue>
using namespace std;
#define MAX_SIZE 5500
struct Edge{
	int v, w;
	int weight;
	bool operator<(const Edge&a)const{   //两个 const不可以掉,妈的被坑几次
		return weight>a.weight;
	}
};
int parent[MAX_SIZE];     
int getParent(int i);
bool Union(int i, int j);
int Kruskal(priority_queue<Edge>&E);    //这里非得传递引用,不然会Wa

int main(){
	Edge edge;
	priority_queue<Edge>E;
	int Q, N, i, j, v, w, ans;
	while (scanf("%d",&N)!=EOF){
		memset(parent, -1, sizeof(parent));
		for (i = 1; i <= N; i++){
			edge.v = i;
			for (j = 1; j <= N; j++){
				scanf("%d",&edge.weight);
				edge.w = j;
				if (i < j)
					E.push(edge);   //入堆    
			}
		}
		scanf("%d",&Q);
		for (i = 0; i < Q; i++){
			cin >> v >> w;
			Union(v, w);
		}
		ans = Kruskal(E);
		printf("%d\n",ans);
	}
	return 0;
}
int Kruskal(priority_queue<Edge>&E){
	int  cost = 0;
	Edge edge;
	while (!E.empty()){         //i为以收录边的数量
		edge = E.top();
		E.pop();               //删除
		if (!Union(edge.v, edge.w))   //判断此两个顶点是否在一个连通集,不在就合并并返回false
			cost += edge.weight;
	}
	return cost;
}
int getParent(int i){
	if (parent[i] == -1)
		return i;
	return parent[i] = getParent(parent[i]);
}
bool Union(int i, int j){
	i = getParent(i);
	j = getParent(j);
	if (i == j)
		return true;
	parent[i] = j;
	return false;

2.使用Prime算法

       由于题目中给定了一些边,处理的办法是将这些边权值设置为0.定义一个visit[i]数组记录顶点i是否在树中。

#include<iostream>
using namespace std;
#define MAX_SIZE 105
int parent[MAX_SIZE];            //记录父节点
int E[MAX_SIZE][MAX_SIZE];       //边集合
int Prim(int N);
bool visit[MAX_SIZE];            //判断该顶点是否在集合里面
int main(){
	int Q, N, i, j, v, w, ans;
	while (scanf("%d",&N)!=EOF){
		memset(visit, 0, sizeof(visit));    //初始化
		for (i = 1; i <= N; i++)
		for (j = 1; j <= N; j++)
			scanf("%d",&E[i][j]);            //输入边
		scanf("%d", &Q);
		for (i = 0; i < Q; i++){
			scanf("%d%d",&v,&w);
			E[v][w] = E[w][v] = 0;     //已建好将权值设为0
		}
		//-----------------------
		ans = Prime(N);
		printf("%d\n",ans);
	}
	return 0;
}
int Prim(int N){
	int i, j, k,pos,lmin,ans;
	parent[1] =-1;         //设置根节点
	visit[1] = 1;          //表示顶点 1已加入树中
	int LowCost[MAX_SIZE];
	for (i = 1; i <= N; i++)
		LowCost[i] = INT_MAX;      //初始化
	ans = 0;
	j = 1;
	for (i = 2; i <= N; i++){
		/*j表示上一次加入集合的顶点*/
		for (k = 1; k <= N; k++){
			if (!visit[k] && LowCost[k] > E[k][j]){   //更新j的邻接点
				parent[k] = j;           //将k父节点暂定为 j
				LowCost[k] = E[k][j];
			}
		}
		lmin = INT_MAX, pos = 0;
		for (k = 1; k <= N; k++){       //找出不在树中且离树距离最小的点
			if (!visit[k]&&lmin > LowCost[k]){
				pos = k;
				lmin = LowCost[k];
			}
		}
		visit[pos] = 1;      //加入树中
		ans += E[pos][parent[pos]];  //加入边
		j = pos;       //更新j
	}
	return ans;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值