题目链接:
http://poj.org/problem?id=2349
解题思路:
最小生成树问题,但是理解起来,非常费解。。。
用prim算法求解:
先求出每两个顶点之间的距离,(注意:是double类型的),然后用普里姆算法(Prim)求最小生成树。由于无线电的数目已给出m,需要把最小生成树分成m份,即删除m-1条边,得到m个连通分量。关键是删除哪些边呢,题目要求最小的D,故把构成最小生成树的边从大到小排序,删除前m-1条边,第m条边即所要求的最小D。
用kruskal算法求解:
P个哨所最多用P-1条边即可连起来,而S颗卫星可以代替S-1条边,基于贪心思想,代替的边越长,求得的D就越小。所以可以用一个数组保存加入最小生成树的边的长度,共有P-1条边,把前S-1条较长的边代替掉,剩下的边中最长的即为所求,即dis[(P-1) - (S-1) - 1] = dis[P-S-1]。
只要思路清晰,就可以很容易把这道题A了。
注意事项:
用g++交,用%.2f输出
用C++交,用%.2lf输出
不然会wrong。。。
AC代码(prim):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define INF 0xfffffff
using namespace std;
struct node{
double x,y;
}no[505];
int s,p,ans;
double edge[505][505];
double dis[505];
double temp[505];
int vis[505];
double dist(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool cmp(double x,double y){
return x > y;
}
void prim(int cur)
{
int i, j, tmp;
memset(vis, 0, sizeof(vis));
for(i = 1; i <= p; i++)
dis[i] = edge[cur][i];
vis[cur] = 1;
for(i = 2; i <= p; i++)
{
double Min = INF;
for(j = 1; j <= p; j++)
{
if(!vis[j] && dis[j] < Min)
Min = dis[tmp = j];
}
vis[tmp] = 1;
temp[ans++] = Min;
for(j = 1; j <= p; j++)
{
if(!vis[j] && dis[j] > edge[tmp][j])
dis[j] = edge[tmp][j];
}
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&s,&p);
for(int i = 1; i <= p; i++)
scanf("%lf%lf",&no[i].x,&no[i].y);
for(int i = 1; i <= p; i++){
edge[i][i] = 0;
for(int j = 1; j < i; j++){
edge[i][j] = edge[j][i] = dist(no[i].x,no[i].y,no[j].x,no[j].y);
}
}
ans = 0;
prim(1);
sort(temp,temp+ans,cmp);
printf("%.2f\n",temp[s-1]);
}
return 0;
}
AC代码(kruskal):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define INF 0xfffffff
using namespace std;
struct node{
double x,y;
}no[550];
int s,p,m,ans;
double dis[505];
int pa[505];
double dist(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
struct Edge
{
int u,v;
double w;
}edge[1000005];
bool cmp(Edge a,Edge b)
{
return a.w < b.w;
}
int findset(int x)
{
if(pa[x] != x)
pa[x] = findset(pa[x]);
return pa[x];
}
void kruskal()
{
int i,u,v,cnt = p;
for(i = 0; i < m; i++)
{
u = findset(edge[i].u);
v = findset(edge[i].v);
if(u != v)
{
dis[ans++] = edge[i].w;
pa[v] = u;
if(--cnt == 1)
break;
}
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&s,&p);
for(int i = 1; i <= p; i++)
scanf("%lf%lf",&no[i].x,&no[i].y);
m = 0;
for(int i = 1; i <= p; i++)
pa[i] = i;
for(int i = 1; i <= p; i++){
for(int j = 1; j <= i; j++){
double tmp = dist(no[i].x,no[i].y,no[j].x,no[j].y);
edge[m].u = i;
edge[m].v = j;
edge[m++].w = tmp;
edge[m].u = j;
edge[m].v = i;
edge[m++].w = tmp;
}
}
sort(edge,edge+m,cmp);
ans = 0;
kruskal();
printf("%.2f\n",dis[p-s-1]);
}
return 0;
}