目录
1 问题描述
问题描述
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。
2 解决方案
本题主要考查DFS搜索和剪枝,具体解释请看注释。
具体代码如下:
import java.util.Scanner;public class Main { public static int n, m, k; public static double[][] distance; //distanc[i][j]表示第i个村庄到第j个邮局的直线距离 public static int[] answer; //存放最终选中的k个邮局编号 public static double minSum; //表示选中k个邮局的最小距离和 public void init() { distance = new double[n + 1][m + 1]; answer = new int[k + 1]; minSum = Double.MAX_VALUE; } /* * 参数count:表示已经选中的邮局个数 * 参数current:表示DFS目前遍历到的邮局编号 * 参数minDis:minDis[i]表示村庄i到已选中邮局中的最短距离 * 参数tempAns:存放目前遍历过程中已经选中的邮局编号 */ public void dfs(int count, int current, double[] minDis, int[] tempAns) { if(count == k) { //当已经选中k个邮局时 double tempSum = 0; for(int i = 1;i <= n;i++) tempSum += minDis[i]; if(tempSum < minSum) { minSum = tempSum; for(int i = 1;i <= k;i++) answer[i] = tempAns[i]; } return; } else if(k - count <= m - current && current <= m) { //表示在剩下未选中的邮局个数多于还需要选中的邮局个数 boolean judge = false; //用于判断当前选中的邮局current+1是否符合要求 double[] minDis1 = new double[n + 1]; //用于保存当前minDis数组中值 for(int i = 1;i <= n;i++) minDis1[i] = minDis[i]; for(int i = 1;i <= n;i++) { if(minDis[i] > distance[i][current + 1]) { minDis[i] = distance[i][current + 1]; judge = true; } } tempAns[count + 1] = current + 1; if(judge == true) //邮局current + 1符合要求,选中个数加1继续搜索 dfs(count + 1, current + 1, minDis, tempAns); dfs(count, current + 1, minDis1, tempAns); //直接舍弃邮局current+1,进行下一次搜索 } } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); n = in.nextInt(); m = in.nextInt(); k = in.nextInt(); test.init(); double[][] point = new double[n + 1][2]; //存放n个村庄的位置坐标 for(int i = 1;i <= n;i++) { point[i][0] = in.nextDouble(); //村庄横坐标 point[i][1] = in.nextDouble(); //村庄纵坐标 } for(int j = 1;j <= m;j++) { double x = in.nextDouble(); //邮局横坐标 double y = in.nextDouble(); //邮局纵坐标 for(int i = 0;i <= n;i++) //计算1~n村庄到邮局j的直线距离 distance[i][j] = Math.sqrt((point[i][0]-x)*(point[i][0]-x) + (point[i][1]-y)*(point[i][1]-y)); } int[] tempAns = new int[k + 1]; double[] minDis = new double[n + 1]; //minDis[i]表示第i个村庄到已选中的邮局中的最小距离 for(int i = 1;i <= n;i++) minDis[i] = Double.MAX_VALUE;//初始化第i个村庄到选中邮局中最小距离为最大值 test.dfs(0, 0, minDis, tempAns); for(int i = 1;i <= k;i++) System.out.print(answer[i]+" "); }}
参考资料:
1.