时间限制: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;
}