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");
}
}