2015_1_4_graph_最小生成树

1000. Highways
Total: 1674 Accepted: 405
   
Time Limit: 1sec    Memory Limit:32MB
Description
The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has no public highways. So the traffic is difficult in Flatopia. The Flatopian government is aware of this problem. They're planning to build some highways so that it will be possible to drive between any pair of towns without leaving the highway system. 


Flatopian towns are numbered from 1 to N. Each highway connects exactly two towns. All highways follow straight lines. All highways can be used in both directions. Highways can freely cross each other, but a driver can only switch between highways at a town that is located at the end of both highways. 


The Flatopian government wants to minimize the length of the longest highway to be built. However, they want to guarantee that every town is highway-reachable from every other town.
Input
The first line is an integer N (3 <= N <= 500), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 65536]) between village i and village j. 
Output
You should output a line contains an integer, which is the length of the longest road to be built such that all the villages are connected, and this value is minimum.
This problem contains multiple test cases!
The first line of a multiple input is an integer T, then a blank line followed by T input blocks. Each input block is in the format indicated in the problem description. There is a blank line between input blocks.
The output format consists of T output blocks. There is a blank line between output blocks.
Sample Input
 Copy sample input to clipboard
1

3
0 990 692
990 0 179

692 179 0

Sample Output

692

最小生成树问题,加入最少的边使所有点连通且权重和最小,输出其中最长的那一条边权重。

一是Kruskal,把所有边权重进行排序,在不形成回路的前提下从小到大取出加入图。需要考虑如何在对权重进行排序的同时保持边的两端点等属性随之联动。

u[]存放左端点、v[]存放右端点、w[]存放权重、r[]用于存放排序结果,通过r的索引找到边,这样就能保证属性不变。

怎么判断回路?并查集p[],-1表示该点为根结点,否则就找对应x为其父节点索引,如果两点成环那么最终查找到的祖宗结果相等。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int u[125010], v[125010], w[125010], r[125010], parent[510];

bool cmp(const int &x, const int &y){
    return w[x] < w[y];
}

void union_roots(int x, int y){     //合并两个根结点,意味着必须有p[x],p[y]<0
    if (parent[x] < parent[y])      //x树更高 
        parent[y] = x;
    else if (parent[x] > parent[y]) //y树更高 
        parent[x] = y;
    else{
        parent[y] = x;      //高度相等就直接把后者往前挂 
        parent[x] -= 1;     //注意x树高度up,即负的更多 
    }
}

int find(int x){
    return (parent[x] < 0)? x : parent[x] = find(parent[x]);
}

int main(){
    int t;
    while (scanf("%d", &t) != EOF){
        bool first = true;
        while (t--){
            int n, index = 1;
            scanf("%d", &n);
            memset(parent, -1, sizeof(parent));
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= n; j++){
                    scanf("%d", &w[index]);
                    if (j > i){
                        u[index] = i;
                        v[index] = j;
                        r[index] = index;
                        index += 1;
                    }   
                }
            }
            sort(r + 1, r + index, cmp);
            int ans = 0;
            for (int i = 1; i < index; i++){
                int e = r[i];
                int x = find(u[e]), y = find(v[e]);
                if (x != y){
                    if (ans < w[e])
                        ans = w[e];
                    union_roots(x, y);
                }
            } 
            if (first){
                first = false;
                printf("%d\n", ans);
            }   
            else
                printf("\n%d\n", ans);  
        }
    }
    return 0;
}                                 


二是prim算法:图中任取一点作为起始点,标记该点的权重为0表示已经放入了树。然后将与最小生成树邻接的点标记出权重,具体为邻接点权重+边的权重,当比原先的值小时就要刷新为较小的权重值。刷新完成后就把这些未加入树的邻接点当中权重最小的放入树。

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

//Prim solution
int weight[505][505];       //邻接矩阵,存放边的权重 
int visit[505];             //是否已经放入树
int d[505];                 //各顶点的权重 
const int INF = 65540;

int main(){
    int t;
    while (scanf("%d", &t) != EOF){
        bool first = true;
        while (t--){
            int n, ans = 0;
            scanf("%d", &n);        //n个结点 
            memset(weight, 0, sizeof(weight));
            memset(visit, 0, sizeof(visit));
            memset(d, INF, sizeof(d));
            d[1] = 0;               //第1个点权重设置为0,只要不是INF就与树有连接了 
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= n; j++){
                    scanf("%d", &weight[i][j]);
                }
            }
            int start = 1, end = 1;
            for (int i = 1; i <= n; i++){
                int min = INF;
                for (int j = 1; j <= n; j++){   //可优化? 
                    if (visit[j] == 0 && min > d[j]){
                        end = j;        //没有加入树且权重比当前邻接点中最小权重还小的 
                        min = d[j];     //定位到该点并刷新邻接点的最小权重 
                    }
                }
                visit[end] = 1;         //最小权重对应点加入树 
                d[end] = 0;
                
                //printf("newly add: %d,  weight: %d\n", end, weight[start][end]);
                
                if (ans < min)
                    ans = min;  //刷新树中最长路径 
                
                start = end;            //从这点往外扩 
                
                for (int k = 1; k <= n; k++){
                    if (weight[start][k] != 0){     //两点存在边 
                        if (d[k] > d[start] + weight[start][k]) 
                            d[k] = d[start] + weight[start][k]; 
                        //若从新加入树的这个点到目的点的权重+连接两点的边的权重
                        //都比该目的点先前的权重小(可能是INF) 刷新 
                    }
                }
            } 
            if (first){
                first = false;
                printf("%d\n", ans);
            }
            else
                printf("\n%d\n", ans);
        }
    }
    return 0;
}                                 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值