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