题目链接:
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; }