HDU_3371 Connect the cities(最小生成树)

Connect the Cities

Description

In 2100, since the sea level rise, most of the cities disappear. Though some survived cities are still connected with others, but most of them become disconnected. The government wants to build some roads to connect all of these cities again, but they don’t want to take too much money.

Input

The first line contains the number of test cases.

Each test case starts with three integers: n, m and k. n (3 <= n <=500) stands for the number of survived cities, m (0 <= m <= 25000) stands for the number of roads you can choose to connect the cities and k (0 <= k <= 100) stands for the number of still connected cities.

To make it easy, the cities are signed from 1 to n.

Then follow m lines, each contains three integers p, q and c (0 <= c <= 1000), means it takes c to connect p and q.

Then follow k lines, each line starts with an integer t (2 <= t <= n) stands for the number of this connected cities. Then t integers follow stands for the id of these cities.

Output

For each case, output the least money you need to take, if it’s impossible, just output -1.

Sample Input

1

6 4 3

1 4 2

2 6 1

2 3 5

3 4 33

2 1 2

2 1 3

3 4 5 6

Sample Output

1

题解:

这是一道典型的最小生成树的问题,最小生成树有两种算法,Prim和Kruskal在这道题里面是都适用的,正好趁着这道题总结一下这两种算法。

Prim算法:

基本原理:不断的将节点加入到一个树上(used数组标志),每次找到的下一个点都是距离当前树权值最小的节点;

适用题型:注意到Prim算法以节点为关注点,所以对于节点数较少,边数较多的密集图,应该使用Prim算法;

Kruskal算法:

基本原理:贪心的每次挑选出当前边集中最小的边,将边加入到树上,生成一个联通子图,但注意已生成的树上不能出现环,这点利用并查集实现;

适用题型:边较少的稀疏图;

对于这道题目, (3 <= n <=500)(0 <= m <= 25000),是一个 稠密图,按理利用Prim会更好一点,但是注意每次都会有K组的节点是相互连通的,对于Prim算法来说将这些信息保存将会用到O(N^2)的复杂度。而对于Kruskal来说加边就很方便,只需要将他们生成一个联通子图。所以这道题两种方法的时间相差不大,但是有助于理解算法的实现与优化。

代码实现:

Prime:


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define MAX_N 510
#define MAX_M 25010
#define MAX_K 110
#define LL long long
#define INF 0x7fffffff

using namespace std;

int N,M,K;
int res,T;
int cost[MAX_N][MAX_N];
int mincost[MAX_N];
int Left[MAX_N];
bool used[MAX_N];
void Prim();
void Check();

int main()
{
    scanf("%d",&T);
    while( T-- ){
        scanf("%d%d%d",&N,&M,&K);
        for( int i = 1; i <= N; i++ ){
            for( int j = 1; j <= N; j++ ){
                cost[i][j] = INF;
            }
            cost[i][i] = 0;
            mincost[i] = INF;
            used[i] = false;
        }
        int a,b,c;
        int num;
        for( int i = 0; i < M; i++ ){
            scanf("%d%d%d",&a,&b,&c);
            cost[a][b] = min(cost[a][b],c);
            cost[b][a] = min(cost[b][a],c);
        }
        for( int i = 0; i < K; i++ ){
            scanf("%d",&num);
            for( int j = 0; j < num; j++ ){
                scanf("%d",&Left[j]);
            }
            for( int j = 0; j < num; j++ ){
                for( int t = 0; t < num; t++ ){
                    cost[Left[t]][Left[j]] = 0;
                    //压坏算法的最后一根稻草,TLE了无数次,只做了一个这么小的优化就过了。。。
                    //cost[Left[j]][Left[t]] = 0;
                }
            }
        }
        Prim();
        printf("%d\n",res);
    }
    return 0;
}

void Prim()
{
    res = 0;
    //设置1为起始点,但是并没有放入1
    mincost[1] = 0;
    //used[1] = true;
    int times = 0;
    //没必要放入1
//    for( int i = 1; i <= N; i++ )
//        mincost[i] = cost[1][i];
    while( true ){
        int v = -1;
        for( int i = 1; i <= N; i++ ){
            if( !used[i] && (v == -1 || mincost[i] < mincost[v] ) )
                v = i;
        }

        if( v == -1 || mincost[v] == INF )
            break;
        used[v] = true;
        res += mincost[v];
        times++;

        for( int i = 1; i <= N; i++ ){
            mincost[i] = min(mincost[i],cost[v][i]);
        }
    }
    //相当于加了N个点
    if( times != N )
        res = -1;
    return ;
}

Kruskal:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define MAX_N 510
#define MAX_M 25010
#define MAX_K 110
#define MAX_C 1010

using namespace std;

struct node{
    int from,to;
    int cost;
};
int Left[MAX_K];
node edges[MAX_M];
int Father[MAX_N];
int Rank[MAX_N];

int times;
int res;
int N,M,K;
int p,q,c;
int num;

void Ini();
void kruskal();
int Find(int x);
bool Same(int x,int y);
bool Unite(int x,int y);
bool cmp( const node &e1,const node &e2){
    return e1.cost < e2.cost;
}

int main()
{
    scanf("%d",&times);
    while( times-- )
    {
        scanf("%d%d%d",&N,&M,&K);
        Ini();
        for( int i = 0; i < M; i++ )
        {
            scanf("%d%d%d",&p,&q,&c);
            edges[i].from = p;
            edges[i].to = q;
            edges[i].cost = c;
        }
//        memset(Left,0,sizeof(Left));
        for( int i = 0; i < K; i++ )
        {
            int KK;
            scanf("%d",&KK);
            for( int j = 0; j < KK; j++ )
            {
                scanf("%d",&Left[j]);
                //将后面的节点全部连到第一个点上
                if( Unite(Left[j],Left[0]) )
                    num++;
            }
        }
        kruskal();
        if( num != N-1 )
            res = -1;
        printf("%d\n",res);
    }
    return 0;
}

void Ini()
{
    memset(Left,0,sizeof(Left));
    res = 0;
    num = 0;
    for( int i = 1; i <= N; i++ )
    {
        Father[i] = i;
        Rank[i] = 1;
    }
    return ;
}

void kruskal()
{
    sort(edges,edges+M,cmp);
    for( int i = 0; i < M; i++ )
    {
        node temp = edges[i];
        if( Unite(temp.from,temp.to) )
        {
            res+=temp.cost;
            num++;
        }
        //最小生成树只有N-1条边
        if( num == N-1 )
            break;
    }
    return ;
}

//在这里same函数没太大用处,使用的话甚至还是一种时间上的浪费
bool Same(int x,int y)
{
    return Find(x) == Find(y);
}

bool Unite(int x,int y)
{
    x = Find(x);
    y = Find(y);
    if( x == y )
        return false;
    if( Rank[x] < Rank[y] )
        Father[x] = y;
    else
    {
        Father[y] = x;
        if( Rank[x] == Rank[y] )
            Rank[x]++;
    }
    return true;
}

int Find(int x)
{
    int r,k,j;
    r = x;
    while( r != Father[r] )
        r = Father[r];
    k = x;
    while( Father[k] != r )
    {
        j = Father[k];
        Father[k] = r;
        k = j;
    }
    return r;
}
时间: 2024-10-10 23:36:00

HDU_3371 Connect the cities(最小生成树)的相关文章

hdu 3371 Connect the Cities (最小生成树Prim)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3371 题目不难 稍微注意一下 要把已经建好的城市之间的花费定义为0,在用普通Prim算法就可以了:我没有用克鲁斯卡尔算法(Kruskal's algorithm),因为这题数据比较大,而且要处理大量的数据使它为0,怕超时T^T..... 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 5 usi

hdu 3371 Connect the Cities 最小生成树

#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <set> #include <map> #include <string> #include <ma

POJ:3371 Connect the Cities(最小生成树)

http://acm.hdu.edu.cn/showproblem.php?pid=3371 AC代码: /** /*@author Victor /* C++ */ #include <bits/stdc++.h> #include<iostream> #include<algorithm> #include<cstdlib> #include<cstring> #include<cstdio> #include<string

HDU 3371 Connect the Cities 【最小生成树,Prime算法+Kruskal算法】

Connect the Cities Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 17167    Accepted Submission(s): 4335 Problem Description In 2100, since the sea level rise, most of the cities disappear. Tho

hdu oj 3371 Connect the Cities (最小生成树)

Connect the Cities Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 9985    Accepted Submission(s): 2843 Problem Description In 2100, since the sea level rise, most of the cities disappear. Thou

HDU3371 Connect the Cities 【最小生成树Kruskal】

Connect the Cities Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 9856    Accepted Submission(s): 2800 Problem Description In 2100, since the sea level rise, most of the cities disappear. Thou

hdu 3371(Connect the Cities)(最小生成树)

Connect the Cities Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10692    Accepted Submission(s): 3037 Problem Description In 2100, since the sea level rise, most of the cities disappear. Tho

HDU 3371 kruscal/prim求最小生成树 Connect the Cities 大坑大坑

这个时间短 700多s #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; struct node{ int u; int v; int w; }que[100000]; int father[505]; bool cmp(struct node a,struct node b){ return a.w<b.w;

hdu 3371 Connect the Cities

链接:hdu 3371 已知已连通的路的序号,以及未连通的路的费用,求将所有城市连通的最小费用 也是将已连通的路的费用记为0,就转化成了基本最小生成树的题 不过这题数组要开的大点,不然很容易就RE了... #include<cstdio> #include<algorithm> using namespace std; int f[510],n,m; struct stu { int a,b,c; }t[100000]; int cmp(struct stu x,struct st