动态编程:算法解决以下问题?

时间:2012-03-12 12:16:10

标签: algorithm language-agnostic dynamic-programming

我最近完成了以下面试:

'机器人可编程为运行“a”,“b”,“c”......“n”公里,需要t a ,t b ,t c ... t n 分钟。一旦运行到程序设计的公里数,它必须关闭“m”分钟。

在“m”分钟之后,它可以再次被编程为运行另一个“a”,“b”,“c”......“n”公里。

你如何编程这个机器人在最短的时间内达到确切的公里数?'

我认为这是无界knapsack问题的变体,其中大小将是公里数和值,即完成每次伸展所需的时间。主要区别在于我们需要最小化而不是最大化价值。所以我使用了以下解决方案的等价物:http://en.wikipedia.org/wiki/Knapsack_problem#Unbounded_knapsack_problem 我在其中选择最小值。

最后,因为我们需要一个精确的解决方案(如果有的话),在算法为不同距离构建的地图上,我遍历每个机器人的编程距离,找到确切的距离和最短的时间。那些。

我认为机器人在两次运行之间的暂停是一个红色的鲱鱼,你只需要将它包含在你的计算中,但它不会影响所采用的方法。

我可能错了,因为我没有通过考试。我对预期的解决方案没有任何其他反馈。

编辑:也许我毕竟没错,因为不同的原因我失败了。我只是想验证我对这个问题的处理方法。

import static com.google.common.collect.Sets.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public final class Robot {

    static final Logger logger = Logger.getLogger (Robot.class);

    private Set<ProgrammedRun> programmedRuns;
    private int pause;
    private int totalDistance;

    private Robot () {
        //don't expose default constructor & prevent subclassing 
    }


    private Robot (int[] programmedDistances, int[] timesPerDistance, int pause, int totalDistance) {

        this.programmedRuns = newHashSet ();
        for (int i = 0; i < programmedDistances.length; i++) {
            this.programmedRuns.add (new ProgrammedRun (programmedDistances [i], timesPerDistance [i] ) );
        }
        this.pause = pause;
        this.totalDistance = totalDistance;
    }


    public static Robot create (int[] programmedDistances, int[] timesPerDistance, int pause, int totalDistance) {
        Preconditions.checkArgument (programmedDistances.length == timesPerDistance.length);
        Preconditions.checkArgument (pause >= 0);
        Preconditions.checkArgument (totalDistance >= 0);
        return new Robot (programmedDistances, timesPerDistance, pause, totalDistance);
    }

    /**
     * @returns null if no strategy was found. An empty map if distance is zero. A
     * map with the programmed runs as keys and number of time they need to be run
     * as value.  
     * 
     */
    Map<ProgrammedRun, Integer> calculateOptimalStrategy () {

        //for efficiency, consider this case first
        if (this.totalDistance == 0) {
            return Maps.newHashMap ();
        }

        //list of solutions for different distances. Element "i" of the list is the best set of runs that cover at least "i" kilometers
        List <Map<ProgrammedRun, Integer>> runsForDistances = Lists.newArrayList();

        //special case i = 0 -> empty map (no runs needed)
        runsForDistances.add (new HashMap<ProgrammedRun, Integer> () );

        for (int i = 1; i <= totalDistance; i++) {
            Map<ProgrammedRun, Integer> map = new HashMap<ProgrammedRun, Integer> ();
            int minimumTime = -1;
            for (ProgrammedRun pr : programmedRuns) {
                int distance = Math.max (0, i - pr.getDistance ());
                int time = getTotalTime (runsForDistances.get (distance) ) + pause + pr.getTime();
                if (minimumTime < 0 || time < minimumTime) {
                    minimumTime = time;
                    //new minimum found
                    map = new HashMap<ProgrammedRun, Integer> ();
                    map.putAll(runsForDistances.get (distance) );

                    //increase count
                    Integer num = map.get (pr);
                    if (num == null) num = Integer.valueOf (1);
                    else num++;

                    //update map
                    map.put (pr, num);
                }
            }
            runsForDistances.add (map );
        }

        //last step: calculate the combination with exact distance

        int minimumTime2 = -1;
        int bestIndex = -1;
        for (int i = 0; i <= totalDistance; i++) {
            if (getTotalDistance (runsForDistances.get (i) ) == this.totalDistance ) {
                int time = getTotalTime (runsForDistances.get (i) );
                if (time > 0) time -= pause;
                if (minimumTime2 < 0 || time < minimumTime2 ) {
                    minimumTime2 = time;
                    bestIndex = i;
                }
            }
        }

        //if solution found

        if (bestIndex != -1) {
            return runsForDistances.get (bestIndex);
        }

        //try all combinations, since none of the existing maps run for the exact distance
        List <Map<ProgrammedRun, Integer>> exactRuns = Lists.newArrayList();

        for (int i = 0; i <= totalDistance; i++) {
            int distance = getTotalDistance (runsForDistances.get (i) );
            for (ProgrammedRun pr : programmedRuns) {
                //solution found
                if (distance + pr.getDistance() == this.totalDistance ) {
                    Map<ProgrammedRun, Integer> map = new HashMap<ProgrammedRun, Integer> ();
                    map.putAll (runsForDistances.get (i));

                    //increase count
                    Integer num = map.get (pr);
                    if (num == null) num = Integer.valueOf (1);
                    else num++;

                    //update map
                    map.put (pr, num);

                    exactRuns.add (map);
                }
            }
        }

        if (exactRuns.isEmpty()) return null;

        //finally return the map with the best time
        minimumTime2 = -1;
        Map<ProgrammedRun, Integer> bestMap = null;

        for (Map<ProgrammedRun, Integer> m : exactRuns) {
            int time = getTotalTime (m);
            if (time > 0) time -= pause; //remove last pause
            if (minimumTime2 < 0 || time < minimumTime2 ) {
                minimumTime2 = time;
                bestMap = m;
            }
        }

        return bestMap;
    }

    private int getTotalTime (Map<ProgrammedRun, Integer> runs) {
        int time = 0;
        for (Map.Entry<ProgrammedRun, Integer> runEntry : runs.entrySet()) {
            time += runEntry.getValue () * runEntry.getKey().getTime ();
            //add pauses
            time += this.pause * runEntry.getValue ();
        }
        return time;
    }

    private int getTotalDistance (Map<ProgrammedRun, Integer> runs) {
        int distance = 0;
        for (Map.Entry<ProgrammedRun, Integer> runEntry : runs.entrySet()) {
            distance += runEntry.getValue() * runEntry.getKey().getDistance ();
        }
        return distance;
    }

    class ProgrammedRun {
        private int distance;
        private int time;
        private transient float speed;

        ProgrammedRun (int distance, int time) {
            this.distance = distance;
            this.time = time;
            this.speed = (float) distance / time;
        }

        @Override public String toString () {
            return "(distance =" + distance + "; time=" + time + ")";
        }

        @Override public boolean equals (Object other) {
            return other instanceof ProgrammedRun 
                && this.distance == ((ProgrammedRun)other).distance 
                && this.time == ((ProgrammedRun)other).time;
        }

        @Override public int hashCode () {
            return Objects.hashCode (Integer.valueOf (this.distance), Integer.valueOf (this.time));
        }

        int getDistance() {
            return distance;
        }

        int getTime() {
            return time;
        }

        float getSpeed() {
            return speed;
        }
    }

}


public class Main {

    /* Input variables for the robot */

    private static int [] programmedDistances = {1, 2, 3, 5, 10}; //in kilometers
    private static int [] timesPerDistance = {10, 5, 3, 2, 1}; //in minutes

    private static int pause = 2; //in minutes

    private static int totalDistance = 41; //in kilometers

    /**
     * @param args
     */
    public static void main(String[] args) {

        Robot r = Robot.create (programmedDistances, timesPerDistance, pause, totalDistance);

        Map<ProgrammedRun, Integer> strategy = r.calculateOptimalStrategy ();

        if (strategy == null) {
            System.out.println ("No strategy that matches the conditions was found");
        } else if (strategy.isEmpty ()) {
            System.out.println ("No need to run; distance is zero");
        } else {
            System.out.println ("Strategy found:");
            System.out.println (strategy);
        }
    }

}

4 个答案:

答案 0 :(得分:4)

稍微简化,让t i 是机器人运行距离d i 所花费的时间(包括停机时间)。假设t 1 / d 1 ≤...≤t n / d n 。如果t 1 / d 1 明显小于t 2 / d 2 且d 1 < / sub>和要运行的总距离D很大,然后分支和绑定可能优于动态编程。分支和边界解决整数规划公式

最小化Σ i t i x i
受制于 Σ i d i x i = D
∀ix i N

通过使用弛豫的值,其中x i 可以是任何非负实数作为指导。通过将x 1 设置为D / d ,后者很容易被验证为最多(t 1 / d 1 )D 1 和∀i≠1 x i = 0,并且至少(t 1 / d 1 )D,通过设置双程序的唯一变量为t 1 / d 1 。解决放松是绑定步骤;每个整数解都是一个分数解,所以最好的整数解需要时间至少(t 1 / d 1 )D。

分支步骤采用一个整数程序并将其拆分为两个,其解决方案一起覆盖原始的整个解空间。在这种情况下,一个可以有额外的约束x 1 = 0而另一个可以有额外的约束x 1 ≥1。看起来好像这会创建具有边约束的子问题,但事实上,我们可以删除第一个移动,或者通过d 1 减少D并将常数t 1 添加到目标。分支的另一个选项是添加约束x i =⌊D/ d i ⌋或x i ≤⌊D/ d i ⌋ - 1,需要将每次移动的重复次数推广到上限。

分支和绑定的主循环选择子问题,分支的集合之一,计算两个子问题的边界,并将它们放回集合中。蛮力的效率来自这样一个事实:当我们有一个具有特定价值的解决方案时,每个放松价值至少相当多的子问题都可以被抛弃。一旦收集以这种方式清空,我们就有了最佳的解决方案。

分支和绑定以及动态编程的混合是可能的,例如,通过DP计算小D的最佳解决方案,并使用这些值而不是已经解决的子问题上的分支。

答案 1 :(得分:1)

创建大小为m且0到m(m是距离)的数组:

  

a [i] =无限;

     

a [0] = 0;

     

a [i] = min {min {a [i-j] + t j + m,所有 j 在可能的公里数机器人中。和 j≠i }, t i 如果 i 可能是机器人的移动}

a [m]是可能的最低值。您还可以使用b这样的数组来保存a[i]选择。此外,如果[m] ==无限意味着它是不可能的。

编辑:我们可以通过创建有向图来另外解决它,我们的图表依赖于m路径长度,图表标有{0..m}的节点,现在从节点0开始将它连接到所有可能的节点;表示如果你有一公里i你可以连接0和v i 与权重t i ,除了节点0-> x,对于所有其他节点对于j> i,你应该将节点i-> j与权重 t ji + m 连接起来,并且ji在输入公里中可用。现在你应该找到从v 0 到v n 的最短路径。但是这个算法仍然是O(nm)。

答案 2 :(得分:0)

设G为期望的距离。

设n是没有暂停的最长距离。

设L = G / n(整数算术,丢弃分数部分)

设R = G mod n(即上述除法的余数)

让机器人运行它的最长距离(即n)L次,然后以最小量(即最小可用距离等于)的任何距离(a,b,c等)大于R或大于R)

要么我理解错误的问题,要么你都在想它

答案 3 :(得分:0)

我非常相信表现而不是说话。这是一个可能正在寻找你正在寻找的程序。如果它满足您的问题,请告诉我。只需复制,粘贴和运行该程序即可。您当然应该使用自己的数据集进行测试。

import java.util.Arrays;

public class Speed {
/***
 *
 * @param distance
 * @param sprints ={{A,Ta},{B,Tb},{C,Tc}, ..., {N,Tn}}
 */
public static int getFastestTime(int distance, int[][] sprints){
    long[] minTime = new long[distance+1];//distance from 0 to distance
    Arrays.fill(minTime,Integer.MAX_VALUE);
    minTime[0]=0;//key=distance; value=time
    for(int[] speed: sprints)
        for(int d=1; d<minTime.length; d++)
            if(d>=speed[0] && minTime[d] > minTime[d-speed[0]]+speed[1])
                minTime[d]=minTime[d-speed[0]]+speed[1];
    return (int)minTime[distance];
}//

public static void main(String... args){
    //sprints ={{A,Ta},{B,Tb},{C,Tc}, ..., {N,Tn}}
    int[][] sprints={{3,2},{5,3},{7,5}};
    int distance = 21;
    System.out.println(getFastestTime(distance,sprints));
}
}
相关问题