poj 1751 Highways

本文介绍了一种使用Prim算法解决最小生成树问题的方法,并通过一个具体的编程实例详细展示了如何实现该算法。具体包括如何初始化图的数据结构、如何更新边的权重以确保生成的树是最小生成树等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// 题意: n个点,给出一些边(x,y),表示已经连接起来,新建一些边,使得任意两点间连通
// 问新建哪些边使得施工量最小
// 思路: 就是最小生成树问题,把已经连接在一起的顶点的距离设为0

#include<iostream> //最小生成树prim算法
using namespace std;
#define inf 0x7fffffff
const int MAXN=760; //最大顶点数
int n,edge[MAXN][MAXN]; //n记录顶点数
int pos[MAXN][2];
struct MST //最小生成树的边
{
int s,t,w;
}mst[MAXN];
int dist2(int i,int j)
{
int x=pos[i][0]-pos[j][0];
int y=pos[i][1]-pos[j][1];
return x*x+y*y;
}
int q[MAXN];
void Prim()
{
int rear=0,i,j,k;
for(i=0;i<n-1;i++) //顶点下标从0开始,默认选择顶点0加入生成树
{
mst[i].s=0;mst[i].t=i+1;
mst[i].w=edge[0][i+1];
}
for(i=0;i<n-1;++i) //求n-1条边
{
int min_w=inf;
for(j=i;j<n-1;++j)
{
if(mst[j].w<min_w) //求最小权值边
{
min_w=mst[j].w;
k=j;
}
}
swap(mst[k],mst[i]); //最小边移至前端
if(mst[i].w!=0) //说明是新建的边
{
q[rear++]=i; //记录新建的边
}
int v=mst[i].t;
for(j=i+1;j<n-1;++j) //更新后面的边
{
int wei=edge[v][mst[j].t];
if(wei<mst[j].w)
{
mst[j].w=wei;
mst[j].s=v;
}
}
}
for(i=0;i<rear;++i)
printf("%d %d\n",mst[q[i]].s+1,mst[q[i]].t+1);
}
int main()
{
cin>>n;
for(int i=0;i<n;++i)
{
cin>>pos[i][0]>>pos[i][1];
}
for(int i=0;i<n;++i)
{
edge[i][i]=0;
for(int j=i+1;j<n;++j)
{
edge[i][j]=edge[j][i]=dist2(i,j);
}
}
int m,x,y;
cin>>m;
while(m--)
{
cin>>x>>y;
x--; y--;
edge[x][y]=edge[y][x]=0;
}
Prim();
return 0;
}

转载于:https://www.cnblogs.com/mjc467621163/archive/2012/03/30/2425933.html

POJ 1751是一道经典的图论问题,题目描述如下: 给定一个无向图,图中有N个节点和M条边。每条边都有一个权值。现在需要在这个图中增加最少数量的边,使得图中的任意两个节点之间都有一条路径,并且增加边的总权值最小。 这是一个典型的最小生成树(Minimum Spanning Tree, MST)问题。解决这个问题的常用算法是Kruskal算法和Prim算法。这里我们使用Kruskal算法来解决问题。 Kruskal算法的基本步骤如下: 1. 将所有边按权值从小到大排序。 2. 初始化一个并查集(Union-Find Set),将每个节点作为一个独立的集合。 3. 依次选择权值最小的边,如果这条边连接的两个节点不在同一个集合中,则将这条边加入最小生成树,并将这两个节点所在的集合合并。 4. 重复步骤3,直到所有节点都在同一个集合中,或者已经选择了N-1条边(N是节点的数量)。 下面是一个Java实现的示例代码: ```java import java.util.Arrays; import java.util.Scanner; public class Main { static int[] parent; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int N = scanner.nextInt(); int M = scanner.nextInt(); Edge[] edges = new Edge[M]; parent = new int[N + 1]; for (int i = 0; i < M; i++) { int x = scanner.nextInt(); int y = scanner.nextInt(); int cost = scanner.nextInt(); edges[i] = new Edge(x, y, cost); } Arrays.sort(edges); for (int i = 1; i <= N; i++) { parent[i] = i; } int totalCost = 0; int edgeCount = 0; for (Edge edge : edges) { if (find(edge.x) != find(edge.y)) { union(edge.x, edge.y); totalCost += edge.cost; edgeCount++; if (edgeCount == N - 1) { break; } } } System.out.println(totalCost); scanner.close(); } static int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } static void union(int x, int y) { parent[find(x)] = find(y); } static class Edge implements Comparable<Edge> { int x, y, cost; Edge(int x, int y, int cost) { this.x = x; this.y = y; this.cost = cost; } @Override public int compareTo(Edge other) { return this.cost - other.cost; } } } ``` 这个代码首先读取节点和边的数量,然后读取每条边的信息并存储在Edge数组中。接着对边按权值进行排序,并初始化并查集。然后依次选择权值最小的边,如果这条边连接的两个节点不在同一个集合中,则将这条边加入最小生成树,并将这两个节点所在的集合合并。最终输出最小生成树的总权值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值