`
Eastsun
  • 浏览: 309465 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

《程序员》2007第八期之算法擂台

阅读更多
题目描述(手头没书,不知道有没有记错):有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]};
        }
    }
}
  • Postoffice.rar (1.4 KB)
  • 描述: 本文中用到的代码打包
  • 下载次数: 17
分享到:
评论
8 楼 Eastsun 2007-08-22  
哎,看来以后即便没有测试也不能说出来
也不是不想测试,没条件,这边网吧网速巨慢,实在没耐心去下个jdk。
7 楼 抛出异常的爱 2007-08-22  
rtdb 写道
这个帖为什么被投隐藏了??

> 并没有测试

贯出毛病来了。。。。
6 楼 Eastsun 2007-08-21  
rtdb 写道
这个帖为什么被投隐藏了??

> 并没有测试


晕,这种简单的DP算法,即便没有测试,我也有9成的把握可以一次写对。
而且这个主要讨论算法,DP算法已经给出状态描述与转移方程,这个才是核心部分,代码只是补充完备起见。
5 楼 rtdb 2007-08-21  
这个帖为什么被投隐藏了??

> 并没有测试
4 楼 Eastsun 2007-08-20  
Readonly 写道
这个题目有点意思,和小时候做的求最短路程算术题有点类似,是不是有直接的数学解?
另外,实际上村庄分布都是2维的,扩展一下这个题目,输入是一组村庄的x|y坐标,就变得更有实际意义一些了。

对于p=1时确实有直接的结果,我代码中也用到了。
对于一般情况至少我没想到有直接的结论,而且应该也不会有线性时间复杂度的算法。

如果把题目扩展到二维情形,事实上变成一个图论问题了,而且这个时候还得给出两两村庄之间的关系(有直接连接的道路?路程?)

ps:这个帖为什么被投隐藏了??

3 楼 realreal2000 2007-08-20  
我的思路是这样的不知道对不对,假设v=p那么,所有村庄都有一个邮局所以距离是0,所以当p减少1的时候就应该减去村庄之间距离最近的两个村庄任意一个的邮局,类推,减少2的时候找出所有村庄中路途最短的2条,查看然后除去2个村庄,类推。。。时间复杂度,首先对村庄距离排序,n2,然后取出节点,即可
2 楼 Readonly 2007-08-20  
这个题目有点意思,和小时候做的求最短路程算术题有点类似,是不是有直接的数学解?
另外,实际上村庄分布都是2维的,扩展一下这个题目,输入是一组村庄的x|y坐标,就变得更有实际意义一些了。
1 楼 walkandsing 2007-08-20  
用遗传算法也行

相关推荐

    《程序员》9期算法擂台“骑士聚会”源代码

    9期的算法擂台栏目聚焦于“骑士聚会”问题,这是一个涉及算法设计与实现的经典挑战。这个挑战源自于数学游戏,通常与图论或组合优化相关,旨在模拟中世纪骑士在棋盘上的排列方式。 "骑士聚会"问题来源于国际象棋,...

    《程序员》第9期智慧擂台题目文件

    第9期的智慧擂台题目文件,无疑是为程序员们提供了一个提升技能、检验自身技术水平的平台。这次的主题聚焦在了技术文档的编写、程序员的基本素养以及通过实际题目来锻炼和展示程序员的解决问题能力。 技术文档是...

    《程序员编程艺术:面试和算法心得》

    《程序员编程艺术:面试和算法心得》是一本深入探讨编程面试和算法的书籍,主要针对的是准备面试的程序员,特别是那些关注技术深度和广度,以及如何在面试中展现出自己能力的开发者。这本书可能涵盖了从基础数据结构...

    程序员编程艺术:面试和算法心得.pdf

    • 第六章 海量数据处理 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 ...

    程序员代码面试指南 IT名企算法与数据结构题目最优解.zip

    总之,《程序员代码面试指南》是一本全面且深入的资源,它不仅提供了大量的代码示例,还涵盖了理论知识和实践经验,对于希望提升自己算法能力、准备IT名企面试的程序员来说,是一本不可多得的宝典。

    程序员编程艺术系列之经典算法研究 电子书【高清中文带书签】

    A*搜索算法是一种在图中寻找最短路径的算法,它结合了最佳优先搜索和Dijkstra算法的优点,通过评估函数来估算从当前节点到目标节点的代价,从而决定下一个访问的节点。A*算法广泛应用于路径查找和图遍历问题,尤其是...

    2009年程序员杂志第八期

    《2009年程序员杂志第八期》是2009年度出版的一期专业IT杂志,专注于编程技术和软件工程领域的最新动态与深度分析。这期杂志的发布旨在为程序员们提供丰富的学习资源和行业资讯,帮助他们提升技能,了解行业趋势。 ...

    程序员必备算法知识

    在IT行业中,算法是程序员解决问题的关键工具,它们是编程的基础,能够帮助我们高效地处理数据和执行任务。本文档集合中的四个PHP文档深入探讨了程序员应掌握的一些经典算法,这对于提升编程技能至关重要。 首先,...

    程序员实用算法.zip

    在IT行业中,算法是程序员的核心技能之一,它们是解决问题和设计高效程序的基础。"程序员实用算法.zip"这个压缩包很可能包含了一系列与编程相关的算法实现、解释或案例,旨在帮助程序员提升这方面的能力。以下是对...

    程序员的算法趣题.pdf.zip

    《程序员的算法趣题》是一本专门为IT从业者和有志于进入这个领域的学习者准备的算法书籍。它通过一系列有趣且富有挑战性的题目,旨在帮助读者深入理解和掌握计算机科学中的核心算法,提升解决实际问题的能力。这本书...

    程序员实用算法

    数据结构与算法是计算机科学的基础组成部分,对于程序员而言,掌握这些知识对于提升编程能力、解决实际问题以及提高软件开发效率至关重要。数据结构是组织和存储数据的方式,它决定了数据的存取效率和应用范围。而算...

    程序员实用算法源码

    程序员实用算法源码兼容VS2008及更高级的版本。在VS2008中可以直接运行,在VS2010中需先进行转换才能运行。 每个项目文件中,具体参数如何设置,是可以从源码的main函数中获得的,具体可以查看main函数中形如“...

    程序员算法趣题——随书源码

    《程序员算法趣题——随书源码》是一个与算法相关的学习资源,包含了增井敏克著作《程序员算法趣题》中的实例代码。增井敏克是算法领域知名的专家,他的书籍通常深入浅出,旨在帮助程序员提升算法思维和解决实际问题...

    程序员06第2期

    【标题】"程序员06第2期" 涉及的是一期专门针对程序员的杂志或期刊,这通常包含了丰富的IT技术文章、行业动态、编程技巧、软件开发实践等内容。"06第2期"表明这是该系列的第六个年份的第二期,可能包含了当年最前沿...

    程序员实用算法书中的源码

    《程序员实用算法书中的源码》是一本专为程序员设计的算法书籍,旨在提升程序员在实际工作中应用算法的能力。该书由(美)Andrew Binstock和John Rex合作撰写,并由陈宗斌等人翻译成中文。书中涵盖了一系列精选的...

    程序员实用算法——源码

    第8章 任意精度的算术  8.1 构建计算器8.2表示数字  8.3 计算  8.4 加法  8.5 减法  8.6 乘法  8.7 除法  8.8 关于计算器要注意的最后几点  8.9 用于计算平方根的牛顿算法  8.10 分期付款表  ...

    程序员必知必会经典算法

    在IT行业中,算法是程序员的核心技能之一,它们是解决问题和设计高效程序的基石。"程序员必知必会经典算法"这个主题涵盖了编程领域中的重要概念,包括基础算法和数据结构,这些都是C、C++等语言中不可或缺的部分。...

    程序员面试算法大全

    《程序员面试算法大全》是一本面向准备面试的程序员的重要参考资料,涵盖了广泛的算法和数据结构知识。这本书通过详细的代码实现和解题思路,帮助读者提升在面试中的表现,从而提高获得理想职位的机会。以下是对其中...

    2006程序员第八期

    2006程序员第八期

    程序员06第7期 免分

    【标题】:“程序员06第7期 免分”与【描述】:“程序员06第8期 免分” 提供的信息表明,这可能是一份关于程序员知识分享的电子资源,可能包含第7期和第8期的内容。由于“免分”一词,可以推测这些资源是免费提供的...

Global site tag (gtag.js) - Google Analytics