最小生成树Prime hdu 1875

Prime算法的核心步骤是:在带权连通图中V是包含所有顶点的集合, U已经在最小生成树中的节点,从图中任意某一顶点v开始,此时集合U={v},重复执行下述操作:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边,将(u,w)这条边加入到已找到边的集合,并且将点w加入到集合U中,当U=V时,就找到了这颗最小生成树。
其实,算法的核心步骤就是:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边。

题目

Problem Description
相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米。当然,为了节省资金,只要求实现任意2个小岛之间有路通即可。其中桥的价格为 100元/米。
Input
输入包括多组数据。输入首先包括一个整数T(T <= 200),代表有T组数据。
每组数据首先是一个整数C(C <= 100),代表小岛的个数,接下来是C组坐标,代表每个小岛的坐标,这些坐标都是 0 <= x, y <= 1000的整数。
Output
每组输入数据输出一行,代表建桥的最小花费,结果保留一位小数。如果无法实现工程以达到全部畅通,输出”oh!”.

题目给我们的是点的坐标,我们可以用一个结构体表示每个点

typedef struct  HAHAHAHA
{
    int from;//or to
    int x, y;
    double  quan;
    HAHAHAHA* next;
    HAHAHAHA() :x(), y(), from(), quan(), next() {}
    HAHAHAHA(int b, int c) : x(b), y(c), from(), quan(), next() {}
}dian;//点结构体,记录点到点的权,from记录有向变。x,y为坐标(计算权用)

该结构体为两种数据类型共用的:第一种是最小生成有向树(当然也可以生成无向树)根,每个叶和节点的边权(quan)及对应终点(from),兄弟节点(next)。第二种记录坐标(x,y),最小边权(quan),和最小边权边的始点。(from)
然后我们可以开始生成最小树了
我用一个容器装未使用的点
用一个指针数组装已使用的点。
每次往指针数组纳入一个已使用点,就把该点从容器中删除,然后更新容器中的所有边的权重和边的始点

 ///生成树开始 A为vector<dian>,数组Dian为结构体dian指针组gelong获得两点间长度
        dian k ;
        double maxl = 100000;
        int t = 0;
        for (int j = 0; j < A.size(); j++)
        {
           A[j].quan=maxl;
           A[j].from=0;
        }
		 *Dian[0]=A[0];//A[0]纳入已使用点集
        i=0;
        do{
            k = A[t];//新纳入点集点k
            A.erase(A.begin() + t);//删掉A[t]
            maxl = 100000; flag = 1;
            for (int j = 0; j < A.size(); j++)
            {
                double ttt = getlong(A[j], k);
                if (ttt <= A[j].quan &&ttt<=1000 && ttt>=10)
                {
                    A[j].quan = ttt;
                    A[j].from = i;//A[j]当前长度是从点集中from点出发的
                }//用新纳入已用点集的点,历遍每个未纳入点集的点,更新数据
                if (maxl >= A[j].quan && A[j].quan<=1000&&A[j].quan >= 10)
                {
                    maxl = A[j].quan;
                    t = j;
                    flag = 0;
                }//找这些边中最小的哪一个
            }
            if (flag != 0) break;//没找到说明有问题
            *Dian[i + 1] = A[t];//A[t]纳入已使用点集
            conn(Dian[i + 1]->from, i + 1, A[t].quan);//连接有向边
            /*
            void conn(int a, int to, double quan)
			{
   			 	dian* s = dian_creat();
    			s->from = to;
    			s->quan = quan;
   				s->next = Dian[a]->next;
    			Dian[a]->next = s;
			}
      */
       //  conn(i+1, Dian[i+1]->from, Dian[i + 1]->quan);//加上这个就生成无向图
            i++;
        }while(i<n-1);///n个点的最小生成树有n-1边
        ///生成树结束

最后我们历遍树就可以得到答案了

double ans = 0;
int nn = 0;
void dfs(int before, int now)
{
    dian* K = Dian[now];
    if (K == NULL) return;
    K = K->next;
    while (K != NULL)
    {
        if (K->from != before)
        {
            ans += K->quan;
            nn--;
            dfs(now, K->from);
        }
        K = K->next;
    }
}

当然,这题可以用并查集,也可以在生成图的时候直接把答案算出来,
但是我是想写一下用邻接表表示最小生成树的代码。
我这样生成树,会有一个较为完整的数据结构。
ac代码如下

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<math.h>
#include<algorithm>
using namespace std;
//最小生成树Prime算法,关键记录点点间的权
typedef struct  HAHAHAHA
{
    int from;//or to
    int x, y;
    double  quan;
    HAHAHAHA* next;
    HAHAHAHA() :x(), y(), from(), quan(), next() {}
    HAHAHAHA(int b, int c) : x(b), y(c), from(), quan(), next() {}
}dian;//点结构体,记录点到点的权,data为点的标号。x,y为坐标(计算权用)
dian* Dian[105];//已用点集
vector<dian> A;//未用点集
vector<dian>::iterator it;
double ans = 0;
int nn = 0;
dian* dian_creat()
{
    dian* s = (dian*)malloc(sizeof(dian));
    s->next = NULL;
    return s;
}
double  getlong(dian A, dian B)
{
    return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
void list_clear(dian* A)
{
    if (A == NULL) return;
    if (A->next != NULL)
    {
        list_clear(A->next);
    }
    free(A);
}
void all_clear(int n)
{
    A.clear();
    for (int i = 0; i <= n; i++)
    {
        list_clear(Dian[i]);
        Dian[i] = dian_creat();
    }
}
void conn(int a, int to, double quan)
{
    dian* s = dian_creat();
    s->from = to;
    s->quan = quan;
    s->next = Dian[a]->next;
    Dian[a]->next = s;
}
void dfs(int before, int now)
{
    dian* K = Dian[now];
    if (K == NULL) return;
    K = K->next;
    while (K != NULL)
    {
        if (K->from != before)
        {
            ans += K->quan;
            nn--;
            dfs(now, K->from);
        }
        K = K->next;
    }
}
int main()
{
    int t, m, n, b, x, y;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        all_clear(n);
        A.reserve(n + 5);
        int i = 0;
        while (i < n)
        {
            scanf("%d%d", &x, &y);
            A.push_back(dian(x, y));
            i++;
        }
        nn = n; int flag = 1;
        ///生成树开始 A为vector<dian>,数组Dian为结构体dian指针组gelong获得两点间长度
        dian k ;
        double maxl = 100000;
        int t = 0;
        for (int j = 0; j < A.size(); j++)
        {
           A[j].quan=maxl;
           A[j].from=0;
        }
        i=0;
        *Dian[0]=A[0];
        do{
              k = A[t];
            A.erase(A.begin() + t);//删掉t
            maxl = 100000; flag = 1;
            for (int j = 0; j < A.size(); j++)
            {
                double ttt = getlong(A[j], k);
                if (ttt <= A[j].quan &&ttt<=1000 && ttt>=10)
                {
                    A[j].quan = ttt;
                    A[j].from = i;//A[j]当前长度是从点集中from点出发的
                }
                if (maxl >= A[j].quan && A[j].quan<=1000&&A[j].quan >= 10)
                {
                    maxl = A[j].quan;
                    t = j;
                    flag = 0;
                }
            }
            if (flag != 0) break;
         	 *Dian[i + 1] = A[t];
            conn(Dian[i + 1]->from, i + 1, A[t].quan);
            i++;
        }while(i<n-1);
        ///生成树结束
        ans = 0;
        dfs(0, 0);
        if (nn == 1)
            printf("%.1f\n", ans * 100);
        else
            printf("oh!\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值