复习prim算法系列
虽然prim跟dij是差不多的,但是两者还是有一点点差别。
就像我这种菜鸡,只会dij,结果写出来的prim奇怪得很。
这道题我还特意写了个堆优化,但是也没快到那儿去。。。
首先如何看出这是最小生成树的裸题?
在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。这就是prim的做法。
如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路AB,B申请修建公路BC,C申请修建公路CA。则政府将否决其中最短的一条公路的修建申请。
第二个条件有点东西,它确保答案是没有环的。
但是这为什么就是最小生成树呢?
显然,因为他们想修建这些公路,所以AB>AC,BC>BA,CA>CB。
合起来就是AB>=AC,BC>=AB,AC>=BC。
当且仅当这三者相等的时候不等式组成立,而随便去掉一个都没有影响。
所以接下来的任务就是套上prim算法了。
当然不用kruskal算法了,因为边太多了!
这里使用堆优化的prim,还可以。
代码:
#include
#include
#include
const int maxn = 5005;
struct Nodes
{
double x, y;
} s[maxn];
double dist[maxn];
bool vis[maxn];
double ans;
int n;
struct HeapNodes
{
double dist; int u;
HeapNodes(double dist, int u): dist(dist), u(u){}
bool operator < (const HeapNodes &rhs) const
{
return dist > rhs.dist;
}
};
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
return s * ans;
}
double dis(int a, int b)// 直接人工算距离就可以了
{
return sqrt((s[a].x - s[b].x) * (s[a].x - s[b].x) + (s[a].y - s[b].y) * (s[a].y - s[b].y));
}
void prim()
{
std::priority_queue heap;
dist[1] = 0; vis[1] = true;
for(int i = 2; i <= n; i++)
{
dist[i] = dis(1, i);
heap.push(HeapNodes(dist[i], i));// 这是初始化阶段,用1开拓所有的节点
}
while(!heap.empty())
{
HeapNodes u = heap.top(); heap.pop();
if(vis[u.u]) continue;// vis过当然就忽略了
ans += u.dist;// 添加进来
vis[u.u] = true;
for(int i = 1; i <= n; i++)
{
if(!vis[i])
{
if(dis(u.u, i) < dist[i])// 注意这里的写法,我之前写错了
{
dist[i] = dis(u.u, i);
heap.push(HeapNodes(dist[i], i));
}
}
}
}
}
int main()
{
n = read();
for(int i = 1; i <= n; i++) scanf("%lf%lf", &s[i].x, &s[i].y);
prim();
printf("%.2lf\n", ans);
return 0;
}