题目描述(手头没书,不知道有没有记错):有v个在一条直线上的村庄,要在其中的p个村庄上修建邮局。每个村庄将会选择离其最近的村庄收发邮件。现在要选择这p个邮局的修建地址,使得这v个村庄到邮局的总路程尽可能少。
输入:首先是两个正整数v,p,表示村庄个数与邮局个数。
然后是v个不小于零的整数,表示v个村庄的坐标。
输出:p从小到大个用空格分开的整数,表示p个邮局的坐标。
这个题目用简单的重举法显然是行不通的,那样时间复杂度将达到阶乘级别。
我用的是动态规划,时间复杂度为O(n*n*(n+p)).不知道有没有更好的算法。
注意:这个题目的解实际上不唯一,比如v=2,p=1的情形,邮局修在村庄1与村庄2是一样的。[/color]
/**
* &#Postoffice.java
* 算法描述:
* 记m(i,j)为前i个村庄设置j个邮局时最小的总路程,
* s(i,j)为从村庄i到j中(包括i,j)中设置一个村庄时需要的最小路程,则有:
* m(i,j) =min{ m(k,j-1)+s(k,i-1) : j-1<=k<=i-1}
* 则状态总数为 O(n*(p+n)),转移代价为O(n),故总体时间复杂度为O(n*n*(n+p))
* @author Eastsun
* @version 2.0 2008/8/27
*/
package eastsun.algorithm;
import java.util.*;
public class Postoffice {
private Postoffice() {
}
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int v = s.nextInt();
int p = s.nextInt();
int[] villages = new int[v];
for (int index = 0; index < v; index++) {
villages[index] = s.nextInt();
}
for (int post : solve(villages, p)) {
System.out.println(post + " ");
}
}
/**
* 求解
* @param villages 村庄坐标
* @param p 邮局个数
* @return postoffices 按升序排列的邮局坐标
*/
public static int[] solve(int[] villages, int p) {
Arrays.sort(villages);
int v = villages.length;
//对于p<=2时,特殊处理
if (p <= 2) {
return solveX(villages, p);
}
//simple[i][j] 表示从村庄i到j(包括i,j)中放置一个邮局时这些村庄要走的路程
int[][] simple = new int[v][v];
for (int i = 0; i < v; i++) {
for (int j = i; j < v; j++) {
//此时,将邮局修在中间那个村庄可以得到最小路程
int pos = (i + j) / 2;
for (int k = i; k <= j; k++) {
simple[i][j] += k <= pos ? villages[pos] - villages[k] : villages[k] - villages[pos];
}
}
}
//min[i][j] 表示在villages[0...i-1]中修建j个邮局时最小的路程
int[][] min = new int[v + 1][p + 1];
//trace[i][j] 表示这j个邮局中最后一个邮局影响村庄的起始位置
int[][] trace = new int[v + 1][p + 1];
for (int i = 1; i <= v; i++) {
min[i][1] = simple[0][i - 1];
}
for (int j = 2; j <= p; j++) {
for (int i = j; i <= v; i++) {
min[i][j] = Integer.MAX_VALUE;
for (int k = j - 1; k <= i - 1; k++) {
if (min[i][j] > min[k][j - 1] + simple[k][i - 1]) {
min[i][j] = min[k][j - 1] + simple[k][i - 1];
trace[i][j] = k;
}
}
}
}
int[] postoffices = new int[p];
int end = v - 1;
for (int index = p - 1; index >= 0; index--) {
int begin = trace[end + 1][index + 1];
postoffices[index] = villages[(begin + end) / 2];
end = begin - 1;
}
return postoffices;
}
/**
* 对邮局个数为1或2时的处理
* 此时对应的时间复杂度为O(1) 与O(n*n)
*/
private static int[] solveX(int[] villages, int p) {
int v = villages.length;
if (p == 1) {
return new int[]{villages[(v - 1) / 2]};
} else {
int pos = 0;
int min = Integer.MAX_VALUE;
for (int index = 0; index < v - 1; index++) {
int p1 = index / 2;
int p2 = (index + v) / 2;
int tmp = 0;
for (int k = 0; k < v; k++) {
if (k <= index) {
tmp += k <= p1 ? villages[p1] - villages[k] : villages[k] - villages[p1];
} else {
tmp += k <= p2 ? villages[p2] - villages[k] : villages[k] - villages[p2];
}
}
if (tmp < min) {
min = tmp;
pos = index;
}
}
return new int[]{villages[pos / 2], villages[(pos + v) / 2]};
}
}
}
分享到:
- 2007-08-19 19:55
- 浏览 3259
- 评论(8)
- 论坛回复 / 浏览 (7 / 4057)
- 查看更多
相关推荐
《程序员》9期算法擂台“骑士聚会”源代码
《程序员》第9期智慧擂台题目文件
围绕“面试”、“算法”、“编程”三个主题的程序员编程艺术系列(简称TAOPP系列),从今年4月写第一篇起,至今快有一年。近1年的创作中,写了二十七章,共计22篇文章。这是本人的第4大原创作品,不过与之前微软面试...
程序员实用算法+源码,本书一共七个部分全部下载才可正常解压
• 第六章 海量数据处理 o 6.0 本章导读 o 6.1 关联式容器 o 6.2 分而治之 o 6.3 simhash 算法 o 6.4 外排序 o 6.5 MapReduce o 6.6 多层划分 o 6.7 Bitmap o 6.8 Bloom filter o 6.9 Trie 树 o 6.10 数据库 o 6.11 ...
2009年程序员杂志第八期 (希望大家有收获)
程序员代码面试指南 IT名企算法与数据结构题目最优解,算法全部代码,非常优秀,算法精妙,题目经典,欢迎下载。
程序员代码面试指南 IT名企算法与数据结构题目最优解(书中代码)
程序员实用算法
程序员实用算法 程序员实用算法程序员实用算法 程序员实用算法
PHP程序员算法题汇总,经典算法题仅供学习研究。
程序员实用算法源码兼容VS2008及更高级的版本。在VS2008中可以直接运行,在VS2010中需先进行转换才能运行。 每个项目文件中,具体参数如何设置,是可以从源码的main函数中获得的,具体可以查看main函数中形如“...
2006程序员第八期
程序员2006年第8期PDF.rar
程序员06第8期.pdf
优秀程序员必须知道的32个算法
程序员面试算法大全 有详细代码和结题思路,程序员面试必看的资料!!!
程序员2002- 9期
Kotlin程序员面试算法宝典 Kotlin程序员面试算法宝典 Kotlin程序员面试算法宝典
July在西电的讲座,包括面试中经常考的算法、海量数据处理和机器学习