蓝桥杯练习系统:历届试题 邮局 (详细解释)

时间限制:1.0s   内存限制:256.0MB

   

问题描述

  C村住着n户村民,由于交通闭塞,C村的村民只能通过信件与外界交流。为了方便村民们发信,C村打算在C村建设k个邮局,这样每户村民可以去离自己家最近的邮局发信。

  现在给出了m个备选的邮局,请从中选出k个来,使得村民到自己家最近的邮局的距离和最小。其中两点之间的距离定义为两点之间的直线距离。

输入格式

  输入的第一行包含三个整数n, m, k,分别表示村民的户数、备选的邮局数和要建的邮局数。
  接下来n行,每行两个整数x, y,依次表示每户村民家的坐标。
  接下来m行,每行包含两个整数x, y,依次表示每个备选邮局的坐标。
  在输入中,村民和村民、村民和邮局、邮局和邮局的坐标可能相同,但你应把它们看成不同的村民或邮局。

输出格式

  输出一行,包含k个整数,从小到大依次表示你选择的备选邮局编号。(备选邮局按输入顺序由1到m编号)

样例输入

5 4 2
0 0
2 0
3 1
3 3
1 1
0 1
1 0
2 1
3 2

样例输出

2 4

数据规模和约定

  对于30%的数据,1<=n<=10,1<=m<=10,1<=k<=5;
  对于60%的数据,1<=m<=20;
  对于100%的数据,1<=n<=50,1<=m<=25,1<=k<=10。

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
//n户村民,m个备选邮局,选k个 
int n,m,k;
double home[51][2];//村民数 
double stamp[26][2];//邮局
double s[26][51];//每个备选邮局到每户人家的距离 
double sum=100000001;
int ans[16];

//choice: 已经选取的邮局数,遍历初始值就是 0 个(表示当前未选择任何邮局)
//cur: 当前邮局编号,遍历时,它的值就是从第0个到第m个
//w: 用来记录每一个居民到邮局的最近距离 
// a  :用来记录当前遍历过程中选取的k个邮局

//  初始值 :0 ,0 ,w ,a
void dfs(int choice,int cur, double w[51],int a[16]){
	if(choice==k){//递归出口:当已经选取了k个邮局时

		double su=0;
		for(int i=0;i<n;i++){
			su+=w[i];//统计每个村民到两个邮局最近距离的和
		}
		if(su<sum){//如果该距离小于上一次记录的两个邮局最近距离的和
			sum=su;//就将值赋给sum
			memcpy(ans,a,sizeof(ans));//将记录的k个邮局编号赋给 ans 数组
		}
		return;
	}
	else if(cur<m){// 当前的邮局编号小于m ,即备选的邮局数目 时
			a[choice]=cur;//记录此时的邮局编号

			int ok=0;//在下方的循环中,用来记录

			double t[51];

			memcpy(t,w,sizeof(t));//记录上次的最近距离,当一次递归结束时,下一//次需要从上次的最近距离开始查找
                        
//从第一户居民到第n-1户居民:如果某个(j)居民到当前编号为 cur的邮局距离更近,就将这个距离修改,并且改变ok的值为1,表示有值被修改了
			for(int j=0;j<n;j++){
				if(s[cur][j]<w[j]){
					w[j]=s[cur][j];
					ok=1;
				}
			}
			if(ok){
//如果有值被修改,则找下一个邮局
				dfs(choice+1,cur+1,w,a);
			}
//如果没有,此次的查找不做记录,然后找下一个邮局
			dfs(choice,cur+1,t,a);
		}
}
int main(){
	cin>>n>>m>>k;//输入村民户数,备选邮局数,应该选取的邮局数
	for(int i=0;i<n;i++){
		cin>>home[i][0]>>home[i][1];//输入村民房子的坐标
	}
	for(int i=0;i<m;i++){
		cin>>stamp[i][0]>>stamp[i][1];//输入邮局坐标
		for(int j=0;j<n;j++){//计算每个备选邮局到每户人家的距离 
            //两点间的距离公式: 根号下((x1-x2)的平方+(y1-y2)的平方)
			s[i][j]=sqrt(pow(home[j][0]-stamp[i][0],2)+pow(home[j][1]-stamp[i][1],2));
		}
	}
	double w[51];//用来记录每一个居民到邮局的最近距离
	for(int i=0;i<n;i++){
		w[i]=10000000.1;//将该数组初始化为一个较大的浮点数,便于比较最小距离
	}
	int a[16];//用来记录当前遍历过程中选取的k个邮局(第一个,第二个..按邮局顺序记录)

//第一个0:已经选取的邮局数,遍历初始值就是 0 个(表示当前未选择任何邮局)
//第二个0:当前邮局编号,遍历时,它的值就是从第0个到第m个 
	dfs(0,0,w,a);

	for(int i=0;i<k;i++){
		cout<<ans[i]+1<<ends;//遍历结束,输出满足题意的邮局数
	}
	return 0;	
}

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值