Arctic Network POJ

来源:互联网 发布:苹果7开数据的快捷 编辑:程序博客网 时间:2024/06/11 16:59


Arctic NetworkPOJ - 2349
The Department of National Defence (DND) wishes to connect several northern outposts by a wireless network. Two different communication technologies are to be used in establishing the network: every outpost will have a radio transceiver and some outposts will in addition have a satellite channel.
Any two outposts with a satellite channel can communicate via the satellite, regardless of their location. Otherwise, two outposts can communicate by radio only if the distance between them does not exceed D, which depends of the power of the transceivers. Higher power yields higher D but costs more. Due to purchasing and maintenance considerations, the transceivers at the outposts must be identical; that is, the value of D is the same for every pair of outposts.

Your job is to determine the minimum D required for the transceivers. There must be at least one communication path (direct or indirect) between every pair of outposts.
Input
The first line of input contains N, the number of test cases. The first line of each test case contains 1 <= S <= 100, the number of satellite channels, and S < P <= 500, the number of outposts. P lines follow, giving the (x,y) coordinates of each outpost in km (coordinates are integers between 0 and 10,000).
Output
For each case, output should consist of a single line giving the minimum D required to connect the network. Output should be specified to 2 decimal points.
Sample Input
12 40 1000 3000 600150 750
Sample Output
212.13
思路:这是一个最小生成树的问题,首先求出它的最小生成树,然后用卫星去替换大的边,剩下的边中最大的为所求的长度
理解题目很重要,我一开始就完全没明白这个卫星到底时怎么用的, Any two outposts with a satellite channel 
can communicate via the satellite,这句话我当时就理解成了两个站通过一个卫星可以交流,这样不就是一个卫星可以替代一条边吗,但是但是!!这句话
仔细翻译的话人家的意思是一个站有一个卫星可以通过卫星交流,所以每个站都有一个卫星才行,所以是两个卫星替代一条边

下面用两种不同的求最小生成树的方法来求这个题

方法1:

最小生成树prim算法

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;#define INF 0x3f3f3f3fint num;//样例个数int s,p;//s是卫星个数,p是站台的个数struct node{    double x,y;}pp[550];//记录站台位置的结构体double mp[550][550];//相当于记录地图的二维数组,值为每个点之间的距离double dis[550];//最小生成树最终的起点到各个点的距离int vis[550];//标记数组void Prim(int st){//int s是代表起点,直接从0开始就好了    memset(vis,0,sizeof(vis));//初始化标记数组    int i,j;    for(i = 0; i < p; i++){        dis[i] = mp[st][i];    }//初始化dis数组    dis[0] = 0.0;    vis[0] = 1;//首节点标记    for(i = 1; i < p; i++){//算法核心        double mind = INF;        int u = -1;        for(j = 0; j < p; j++){            if(!vis[j]&&dis[j]<mind){                mind = dis[u=j];            }//找到最小的        }        if(u==-1)break;        vis[u] = 1;//标记        for(j = 0; j < p; j++){            if(!vis[j]&&dis[j]>mp[u][j])//这里的不交不同于最短路径                dis[j] = mp[u][j];//只要离整体近即可,不注重具体哪个点        }    }    //for(i = 0; i < p; i++)printf("%.2f ",dis[i]);    sort(dis,dis+p);//从小到大排序    //printf("p-s%d\n",p-s);    printf("%.2f\n",dis[p-s]);//这里需要重点解释一下,我想了好久才明白,这里和下一个算法的输出是不同的}   //为什么是dis[p-s],因为这里每个下标代表的是从0起点到这个点的距离,如dis[p]是0到最后一个点的距离所以    //dis[p]就是第p-1条边,因为s替代s-1条边,所以(p-1)-(s-1)=p-s这个数就是从后面去掉替换的遍之后的第一个    //最大的边故直接输出int main(){    scanf("%d",&num);    while(num--){        scanf("%d%d",&s,&p);        int i,j;        for(i = 0; i < p; i++){            scanf("%lf%lf",&pp[i].x,&pp[i].y);        }        memset(mp,INF,sizeof(mp));//初始化设为无穷        for(i = 0; i < p; i++){            for(j = i+1; j < p; j++){//用二维数组保存下每个点到点的距离,无向图                mp[i][j] = mp[j][i] = sqrt((pp[i].x-pp[j].x)*(pp[i].x-pp[j].x)+(pp[i].y-pp[j].y)*(pp[i].y-pp[j].y));            }            mp[i][i] = INF;//自己到自己设为无穷大        }        Prim(0);    }    return 0;}
Kruskal算法(使用并查集):

code:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>using namespace std;#define INF 0x3f3f3f3fint pre[550];//并查集使用的父亲数组int s,p;struct node{    double x,y;}pp[550];//站台结构体记录坐标struct edge{    int u,v;//指uv之间    double d;//距离为d}e[550000];//数组开大一些因为正反都要记录double dis[550];void Init(){    int i;    for(i = 0; i < 550; i++)        pre[i] = i;}int Find(int x){//并查集算法    if(x==pre[x])return x;    else return pre[x] = Find(pre[x]);}int Merge(int x,int y){    int fx = Find(x),fy = Find(y);    if(fx!=fy){        pre[fy] = fx;        return 1;    }    else return 0;}bool cmp(edge a,edge b){    return a.d < b.d;}void Kruskal(int cnt){//Kruskal算法    int i;    int m = 0;    int n = p;    //printf("%d",p);    for(i = 0; i < cnt; i++){        if(Merge(e[i].u,e[i].v)){//如果没有相连接,连接            //printf("!!!!!!!!!!!!!!!!!");            dis[m++] = e[i].d;//数组记录下边            //printf("%.2f  ",dis[m-1]);            n--;//n--意思是还剩下n条没完            if(n==1)break;//n变成1说明n-1条边找完了,就终止        }    }//    for(i = 0; i < m; i++)printf("%.2f  ",dis[i]);//    printf("\n%d\n",p-s-1);    printf("%.2f\n",dis[p-s-1]);//这里我想解释一下为什么是p-s-1,上一个算法是p-s,因为上一个是从0开始,0号元素是0到0的距离所以第i号元素就是第i条边所以    //可以直接p-s就是答案,而这个数组从零开始记录,但是零号元素是第一条边,所以正确答案应该是(p-1)-(s-1)的基础上减1才是(p-1)-(s-1)这条边所在的位置}int main(){    int num;    scanf("%d",&num);    while(num--){        Init();        scanf("%d%d",&s,&p);        int i,j;        for(i = 0; i < p; i++){            scanf("%lf%lf",&pp[i].x,&pp[i].y);        }        int cnt = 0;        for(i = 0; i < p; i++){            for(j = i+1; j < p; j++){                e[cnt].u = i;                e[cnt].v = j;                e[cnt++].d = sqrt((pp[i].x-pp[j].x)*(pp[i].x-pp[j].x)+(pp[i].y-pp[j].y)*(pp[i].y-pp[j].y));                e[cnt].u = j;                e[cnt].v = i;                e[cnt++].d = sqrt((pp[i].x-pp[j].x)*(pp[i].x-pp[j].x)+(pp[i].y-pp[j].y)*(pp[i].y-pp[j].y));//储存边的信息,长度,因为无向图,正反储存两遍            }        }        sort(e,e+cnt,cmp);//对边的长度进行排序        Kruskal(cnt);    }    return 0;}





原创粉丝点击