码迷,mamicode.com
首页 > 编程语言 > 详细

poj 3625 Building Roads 最小生成树(prime或kruskal+并查集)(算法归纳)

时间:2015-08-26 22:43:01      阅读:495      评论:0      收藏:0      [点我收藏+]

标签:

Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
Description
Farmer John had just acquired several new farms! He wants to connect the farms with roads so that he can travel from any farm to any other farm via a sequence of roads; roads already connect some of the farms.

Each of the N (1 ≤ N ≤ 1,000) farms (conveniently numbered 1..N) is represented by a position (Xi, Yi) on the plane (0 ≤ Xi ≤ 1,000,000; 0 ≤ Yi ≤ 1,000,000). Given the preexisting M roads (1 ≤ M ≤ 1,000) as pairs of connected farms, help Farmer John determine the smallest length of additional roads he must build to connect all his farms.

Input
* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Two space-separated integers: Xi and Yi
* Lines N+2..N+M+2: Two space-separated integers: i and j, indicating that there is already a road connecting the farm i and farm j.

Output
* Line 1: Smallest length of additional roads required to connect all farms, printed without rounding to two decimal places. Be sure to calculate distances as 64-bit floating point numbers.

Sample Input
4 1
1 1
3 1
2 3
4 3
1 4
Sample Output
4.00

意思很简单:就是给你n.m……n个点的坐标,和m条已经连好了的路线,求最小的生成树,即使每个点都直接或间接的相连。输出生成树的连接的边的长度和。。。
很裸的一道最小生成树的问题。。。
但这里。想对最小生成树的用法归纳下。。。

最小生成树大体上有两种常用的算法。。prime和kruskal算法。两种算法都得区别在于一个对点,一个对边。并没有什么优劣之分,,prime的复杂度与点有关,而kruskal的复杂度与边有关。

kruskal实质上是一种贪心的思想,,它用到了并查集和排序。。首先记录下每条边的两节点及两点之间的长度,(这里用结构体),然后以边排序,从小到大。。从最小的边取出来。如果两节点父节点不同,则加入到最小生成树的边里。合并两点的父节点。一直到最后。。这里对没条边排序再枚举,必然在时间上与边的数量有关。

poj3625:吧边的长度的开方放到已知是生成树的边里去算。。。刚好踩着时间过了。。。1000ms
(这个题的点较多。。没两个点都得计算出距离。。二最小生成树只有n-1条边。。这样可以节约不少时间)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#pragma comment(linker,"/STACK:102400000,102400000")
#define pi acos(-1.0)
#define EPS 1e-6
#define INF (1<<24)
#define mod 1000000007
using namespace std;
int father[1005];
int n,m;
int pp;   //±?êy
struct Point{
    int x,y;
}point[1005];
struct Lu{
    int x,y;
    double dis;
}lu[1000005];

bool cmp(struct Lu a,struct Lu b)
{
    return a.dis-b.dis<EPS;
}
int findfather(int x)
{
    if(x!=father[x])
        father[x]=findfather(father[x]);
    return father[x];
}
void Uion(int x,int y)
{
    int a=findfather(x);
    int b=findfather(y);
    father[a]=b;
}
bool same(int x,int y)
{
    if(findfather(x)==findfather(y)) return true;
    return false;
}
double kruskal()
{
    int i;
    int v=0;
    double res=0.0;
    for(i=0;i<pp;i++)
    {
        if(same(lu[i].x,lu[i].y)==false)
        {
            Uion(lu[i].x,lu[i].y);
            res+=sqrt((double)lu[i].dis);
            v++;
        }
        if(v==n-1-m) break;
    }
    return res;
}
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        int i,j;
        for(i=1;i<=n;i++)
        {
            scanf("%d %d",&point[i].x,&point[i].y);
        }
        pp=0;
        for(i=1;i<=n;i++)
        {
            for(j=i+1;j<=n;j++)
            {
                lu[pp].x=i;
                lu[pp].y=j;
                double ff=(double)(point[i].x-point[j].x)*(point[i].x-point[j].x)+(double)(point[i].y-point[j].y)*(point[i].y-point[j].y);
                lu[pp].dis=ff;
                pp++;
            }
        }
        sort(lu,lu+pp,cmp);
        for(i=0;i<=n;i++) father[i]=i;
        for(i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            Uion(a,b);
        }
        printf("%.2f\n",kruskal());
    }
    return 0;
}

prime算法:是用的是邻接矩阵,,没有边的两点职位INF(极大值),也是从举着里去数,不断维护边,然后跟新记录某点到其他点的最小权值和。。。求最小生成树的prime和求最短路的dijkstra和相似
http://blog.csdn.net/xtulollipop/article/details/47728647
写法上也很相似,因此两算法可以一起学习了。。。

还是也是上边的题:
用prime又写了一下。。79ms。。。好吧。。完胜。。(对于给定点求最小生成树的类型的题,给了边的话。。两种算法应该都没有什么差别)。。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#pragma comment(linker,"/STACK:102400000,102400000")
#define pi acos(-1.0)
#define EPS 1e-6
#define mod 1000000007
#define INF 0x7fffffff
#define inf 0x3f3f3f3f
#define Inf 1e7
typedef long long LL;
using namespace std;
double cost[1005][1005]; //边a,b的权值。
double mincost[1005];   //x点到其他点的最下权值
bool used[1005];        //点是否被用过
int n,m;
struct Point{
    int x,y;
}point[1005];
double prime()
{
    for(int i=1;i<=n;i++) mincost[i]=1e7;
    memset(used,false,sizeof(used));
    mincost[1]=0.0;
    double res=0.0;
    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) break;
        used[v]=true;
        res+=mincost[v];
        for(int i=1;i<=n;i++)
        {
            mincost[i]=min(mincost[i],cost[v][i]);
        }
    }
    return res;
}
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        int i,j;
        for(i=1;i<=n;i++)
        {
            scanf("%d %d",&point[i].x,&point[i].y);
            for(int j = 1; j <= n; ++j) cost[i][j] = INF;
        }
        for(i=1;i<=n;i++)
        {
            cost[i][i]=0.0;
            for(j=i+1;j<=n;j++)
            {
                double ff=(double)(point[i].x-point[j].x)*(point[i].x-point[j].x)+(double)(point[i].y-point[j].y)*(point[i].y-point[j].y);
                ff=sqrt((double)ff);
                if(cost[i][j]>ff) cost[i][j]=cost[j][i]=ff;
            }
        }

        for(i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            cost[a][b]=cost[b][a]=0.0;
        }
        printf("%.2f\n",prime());
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

poj 3625 Building Roads 最小生成树(prime或kruskal+并查集)(算法归纳)

标签:

原文地址:http://blog.csdn.net/xtulollipop/article/details/48009065

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!