`

用递归解决排列问题

阅读更多
引用
一根很细的竹竿27cm,在3cm,7cm,11cm,17cm,23cm 处各有一只蚂蚁,蚂蚁每秒钟走1cm,不能停,可以向左走也可以向右走,当两只蚂蚁碰头后都掉头往回走,蚂蚁不能相互超越,问全部走完竹竿的最长时间和最短时间。 写出程序设计思路

对蚂蚁建模:
属性:速度,在竹竿上的位置,是否向右移动,是否走完
方法:是否向右移动,移动时间后改变状态,是否会和另一只蚂蚁相遇,是否和另一只蚂蚁移动方向相同,改变移动方向,会和另一只蚂蚁相遇的时间。
public class Ant {
    // minus: left; positive: right
    private int speed = 1;

    private float position;

    private boolean over;

    public Ant(float position) {
        this.position = position;
    }

    /**
     * 移动时间后改变状态
     * @param time
     *            the time to set
     */
    public void setTime(float time) {
        if (over) {
            return;
        }
        position = position + speed * time;
        if (position <= 0) {
            over = true;
            position = 0;
        }
        if (position >= 27) {
            over = true;
            position = 27;
        }
    }

    /**
     * @return the loc
     */
    public float getPosition() {
        return position;
    }

    /**
     * @param direction
     *            the direction to set
     */
    public void changeDirection() {
        this.speed *= -1;
    }

    /**
     * @return the right
     */
    public boolean isRight() {
        return this.speed > 0 ? true : false;
    }

    /**
     * @return the over
     */
    public boolean isOver() {
        return over;
    }

    public boolean isSameDirection(Ant ant) {
        return ant.isRight() == isRight();
    }

    public boolean willConflict(Ant ant) {
        return (!ant.isRight()) && isRight();
    }
    
    public float conflict(Ant ant) {
        float f = getPosition();
        float s = ant.getPosition();
        return Math.abs((f - s) / 2);
    }

    public Ant copy() {
        Ant ant = new Ant(this.position);
        ant.over = this.over;
        ant.speed = this.speed;
        return ant;
    }
}

把这个问题它分解成3部分:
  1. 5只蚂蚁初始的移动方向
  2. 根据初始的移动方向,5只蚂蚁走完竹竿的时间
  3. 求出最短和最长时间

第1部分是排列问题,5只蚂蚁初始移动方向共有32种方式,对于排列问题的解决方法我个人认为使用递归比较好,至少是“优雅的”代码,而不是嵌套5个for循环。
/**
     * 
     * @param stack 剩余未被加入排列的元素
     * @param pops 每一个元素是一个排列
     * @author luyang
     */
    private static void permutation(List<Ant> stack, List<List<Ant>> pops) {
        if (stack.isEmpty()) {
            return;
        }
        Ant pop = stack.remove(0);
        if (pops.isEmpty()) {
            List<Ant> pops1 = new ArrayList<Ant>();
            pops1.add(pop);
            pops.add(pops1);

            List<Ant> pops2 = new ArrayList<Ant>();
            Ant clone = pop.copy();
            clone.changeDirection();
            pops2.add(clone);
            pops.add(pops2);
            permutation(stack, pops);
            return;
        }
        List<List<Ant>> copyList = new ArrayList<List<Ant>>();
        for (List<Ant> list : pops) {
            List<Ant> copy = new ArrayList<Ant>();
            copyList.add(copy);
            for (Ant ant : list) {
                Ant clone = ant.copy();
                copy.add(clone);
            }
            list.add(pop.copy());
            Ant clone = pop.copy();
            clone.changeDirection();
            copy.add(clone);
        }
        pops.addAll(copyList);
        permutation(stack, pops);
    }


对于第2部分解决它的算法也是使用递归。
1. 求出2只相邻蚂蚁相遇的最短的时间,这段时间是所有蚂蚁共同的移动时间,也是所有蚂蚁状态保持不变的时间。
2. 2只相邻蚂蚁相遇后,改变他们的状态,累加时间。
3. 重复第1,2步,直到所有蚂蚁走完竹竿。
private static float calculate(List<Ant> ants) {
        float min_time = 13.5F;
        float sum = 0;
        int min_index = 0;
        int same_index = -1;
        for (int i = 0; i < ants.size() - 1; i++) {
            Ant ant = ants.get(i);
            boolean same = ant.isSameDirection(ants.get(i + 1));
            if (!same) {
                same_index = i;
            }
            boolean conflict = ant.willConflict(ants.get(i + 1));
            if (!conflict) {
                continue;
            }
            float time = ant.conflict(ants.get(i+1));
            if (min_time > time) {
                min_time = time;
                min_index = i;
            }
        }
        if (min_time == 13.5F) {
            if (same_index == -1) {
                // same direction
                if (ants.get(0).isRight()) {
                    return 27 - ants.get(0).getPosition() + sum;
                } else {
                    return ants.get(ants.size() - 1).getPosition() + sum;
                }
            } else {
                Ant ant1 = ants.get(same_index);
                Ant ant2 = ants.get(same_index + 1);
                if (ant1.isRight()) {
                    float f1 = 27 - ant1.getPosition();
                    float f2 = ant2.getPosition();
                    return f1 > f2 ? f1 + sum : f2 + sum;
                } else {
                    float f1 = ant1.getPosition();
                    float f2 = 27 - ant2.getPosition();
                    return f1 > f2 ? f1 + sum : f2 + sum;
                }
            }

        }
        sum += min_time;
        for (int i = ants.size() - 1; i >= 0; i--) {
            Ant ant = ants.get(i);
            ant.setTime(min_time);
        }
        ants.get(min_index).changeDirection();
        ants.get(min_index + 1).changeDirection();
        for (int i = ants.size() - 1; i >= 0; i--) {
            Ant ant = ants.get(i);
            boolean over = ant.isOver();
            if (over) {
                ants.remove(i);
            }
        }

        if (!ants.isEmpty()) {
            sum += calculate(ants);
        }
        return sum;
    }


分享到:
评论
3 楼 bill.end 2009-02-26  
bill.end 写道
mumuxinfei 写道
最长24s, 最短11s,
可以把蚂蚁碰撞, 理解为蚂蚁彼此穿越, 不影响结果, 这样的话
程序, 只有两个for循环了, 更加简介了

啊,我没想到,这样问题就简单多了,而且,也证明了我的时间的计算是有问题的。

不是时间的计算是有问题,而是Ant类的right属性是多余的,已经被修改了
2 楼 bill.end 2009-02-26  
mumuxinfei 写道
最长24s, 最短11s,
可以把蚂蚁碰撞, 理解为蚂蚁彼此穿越, 不影响结果, 这样的话
程序, 只有两个for循环了, 更加简介了

啊,我没想到,这样问题就简单多了,而且,也证明了我的时间的计算是有问题的。
1 楼 mumuxinfei 2009-02-25  
最长24s, 最短11s,
可以把蚂蚁碰撞, 理解为蚂蚁彼此穿越, 不影响结果, 这样的话
程序, 只有两个for循环了, 更加简介了

相关推荐

    基于hadoop用并行递归实现排列组合运算

    在解决排列组合问题时,递归算法能够清晰地表达问题的结构。然而,随着`M`值的增大,单机计算所有排列组合变得越来越困难,因为中间结果的数量会迅速增长。例如,当`M=14`时,中间结果的数量就已经超过了1亿,这很...

    递归求全排列.rar 递归求全排列.rar

    在这个压缩包文件"递归求全排列.rar"中,我们探讨的是如何使用递归方法来解决全排列的问题。递归是一种强大的编程技术,它通过函数或方法自身调用自身的方式来解决问题。 全排列是指从n个不同元素中取出m个元素(m...

    易语言递归法取排列组合例程

    递归法是解决此类问题的经典策略,它通过将问题分解成更小的子问题来解决。在排列组合问题中,递归通常用于生成所有可能的序列,而无需存储所有结果。这种方法节省了内存空间,但可能会增加计算时间。 首先,我们...

    递归求解几类排列组合问题

    下面我们将通过四个例子来演示如何使用递归法来解决排列组合问题: 一、类循环组合排列 在这个问题中,我们需要生成所有可能的二进制数。样本输入为4 2,样本输出为0000 0001 0010 0011 0100 0101 0110 0111 1000 ...

    递归解决N皇后

    ### 递归解决N皇后问题 #### 一、问题背景 N皇后问题是一个经典的问题,在一个N×N的棋盘上摆放N个皇后,要求所有皇后彼此之间不能互相攻击(即任意两个皇后不能处于同一行、同一列或相同的对角线上)。此问题不仅...

    排列组合一个练习以及递归输出排列的PPT

    递归算法是一种经典的算法设计方法,它通过将问题分解成更小的子问题,逐步解决问题的方式来解决问题。 在排列组合中,递归算法可以用来输出所有可能的排列序列。该算法的基本思路是:从集合中选择一个元素,放入...

    递归解决汉诺塔问题[java/Eclipse]

    总的来说,汉诺塔问题的解决方案结合了递归算法和Java Swing的图形化能力,提供了一种直观的方式来理解和演示这一经典问题。在Eclipse中实现这样的程序,可以帮助学习者深入理解递归思想,并掌握如何在实际项目中...

    易语言源码递归法取排列组合易语言源码例程.rar

    易语言源码递归法取排列组合易语言源码例程.rar 易语言源码递归法取排列组合易语言源码例程.rar 易语言源码递归法取排列组合易语言源码例程.rar 易语言源码递归法取排列组合易语言源码例程.rar 易语言源码递归法...

    案例排列组合(递归)

    总的来说,理解和掌握排列组合及递归的使用是编程能力的重要组成部分,它们在解决问题时能提供强大的工具。通过`TestCase_test`中的测试用例,你可以进一步巩固这方面的知识,并提高自己的编程技巧。

    递归算法实现随机串和全排列的生成

    在编程领域,递归算法是一种强大的工具,它通过函数或方法自我调用来解决复杂问题。在给定的“递归算法实现随机串和全排列的生成”主题中,我们将深入探讨递归在生成随机字符串和全排列问题中的应用。这里我们将主要...

    c#编写的代码包括递归的排列和半数集,动态规划的导弹问题,贪心算法的找零钱问题

    - **递归排列**:递归是一种函数自我调用的技术,用于解决具有自相似性质的问题。在排列问题中,递归可以用来生成一个集合的所有可能排列。例如,如果集合包含重复元素,我们需要处理这些重复以避免生成重复的排列...

    可以节省(n-1)!次递归的排列生成工具类(java)

    传统的递归方法虽然能解决问题,但在某些情况下会变得非常低效,尤其是当n较大时。 ### 2. **递归优化技巧** 给定文件中提到的方法可以“节省(n-1)!次递归”。这意味着通过某种方式减少了计算过程中的递归调用次数...

    在JAVA中用递归的方法解决汉诺塔问题

    ### 汉诺塔问题及其Java递归解决方案 #### 一、汉诺塔问题概述 汉诺塔(Tower of Hanoi)是一个源自古印度的古老传说中的数学益智游戏,通常用于教授递归算法的概念。游戏包含三个杆(通常标记为A、B、C)以及若干...

    圆排列问题-回溯法-排列集

    在这个java程序中,我们使用回溯法来解决圆排列问题,并使用递归的方式来搜索所有可能的排列,这可以提高搜索的效率。 圆排列问题的解决思路可以分为以下几步: 1. 生成所有可能的排列 2. 计算每个排列的总长度 3....

    java m取n 重复 不重复 排列组合 for循环嵌套递归

    - **递归**:利用递归函数可以方便地处理嵌套问题,适合解决这类组合问题。 #### 三、具体实现 ##### 3.1 不重复排列组合 ```java // 不重复排列组合 public static int loop(int start, int end, char[] chs, ...

    c++用递归方法求解汉诺塔问题

    以下是一个简单的C++代码实现,展示了如何使用递归解决汉诺塔问题: ```cpp #include using namespace std; void moveDisk(int n, char fromRod, char interRod, char toRod) { if (n &gt; 0) { // 将n-1个盘子从...

    算法分析 递归算法 n个数据的排列数

    总的来说,递归算法是解决排列问题的有效工具,通过对递归的理解和掌握,我们可以更好地处理各种组合和排列问题,为编程提供更高效、更优雅的解决方案。在实际编程中,理解并熟练运用递归算法对于提升算法能力至关...

    递归九讲2021 7-9.zip

    在这一章中,主要探讨了如何使用递归解决排列类问题。排列是指从n个不同元素中取出m个元素的所有可能的组合方式,而递归是一种自然的方式来处理这类问题。通过定义基本情况(即最简单的排列)和递归步骤(将问题分解...

Global site tag (gtag.js) - Google Analytics