题目地址:http://poj.org/problem?id=2421
题意:给你n个点,然后求最小生成树。特殊之处在于有一些点之间已经连上了边。
思路:使用kruscal算法,对于已经有边的点,特殊标记一下,加边的时候把这些边的权值赋值为0即可。这样就可以既保证这些边一定存在,又保证了所求的结果正确。
#include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> #define N 100 using namespace std; int father[N]; struct edge{ int lp,rp,value; }ee[N*N];//结构体数组来存储边的顶点和权值 int map[N][N],flag[N][N],numedge,n; int i,j; bool cmp(edge a,edge b){ return a.value < b.value; } //按权值来排序 int find(int x){ if(x == father[x]) return father[x]; return find(father[x]); } bool merge(int x,int y){ int fx = find(x); int fy = find(y); if(fx == fy){ return false; } else{ father[fx] = fy; //加入同一集合 return true; } } //并查集的查找 int kruskal(){ sort(ee,ee+numedge,cmp); //排序 for(i = 1; i <= n; ++i) father[i] = i; //初始化并查集 int sum = 0; for(j = 0; j < numedge; ++j){ int lx = ee[j].lp; int rx = ee[j].rp; if(merge(lx,rx)){ //判断是否在同一集合中,若不是则加入同一集合并且将权值相加 sum += ee[j].value; } } return sum; } int main(){ while(scanf("%d",&n) != EOF){ for(i = 1; i <= n; ++i){ for(j = 1; j <= n; ++j) scanf("%d",&map[i][j]); } int m,x,y; scanf("%d",&m); while(m--){ scanf("%d%d",&x,&y); flag[x][y] = 1; } //对于已有边相连的节点将其标志数组设为1 numedge = 0; for(i = 1; i < n; ++i){ for(j = i + 1; j <= n; ++j){ if(flag[i][j]){ ee[numedge].lp = i; ee[numedge].rp = j; ee[numedge].value = 0; numedge++; //对于已访问过的节点来说,将其边的权值置0,但是边的数量+1 } else{ ee[numedge].lp = i; ee[numedge].rp = j; ee[numedge].value = map[i][j]; numedge++; //对于未访问的节点,将其存储在map数组中的边的权值赋给结构体中的权值,边的数量++ } } } int ans = kruskal(); //通过编写的函数将所有结构体数组中的元素按权值排序,在判断不为同一集合后依次从小到大将权值相加,并将相加过的边的顶点归为同一集合 printf("%d ",ans); //将统计后的最小权值之和输出 } return 0; }