题目地址: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\n",ans);   //将统计后的最小权值之和输出
    }  
    return 0;  
}