Динамическое программирование: Алгоритм решения следующей задачи?

Недавно я выполнил следующее упражнение на собеседовании:

«Робота можно запрограммировать на пробежку «а», «б», «в»… «n» километров, и он занимает t a, t b, t c... t nминут соответственно. Как только он пробежит запрограммированные километры, его нужно выключить на «m» минут.

Через «m» минут его можно снова запрограммировать на прохождение еще «a», «b», «c»… «n» километров.

Как бы вы запрограммировали этого робота, чтобы он прошел точное количество километров за минимальное время?

Я думал, что это вариация задачи о неограниченном рюкзаке, в которой размером будет количество километров, а значением будет время, необходимое для завершения каждого участка. Основное отличие состоит в том, что нам нужно минимизировать, а не максимизировать значение. Поэтому я использовал эквивалент следующего решения: 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 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 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 > runsForDistances = Lists.newArrayList();

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

        for (int i = 1; i <= totalDistance; i++) {
            Map map = new HashMap ();
            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 ();
                    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 > 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 map = new HashMap ();
                    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 bestMap = null;

        for (Map 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 runs) {
        int time = 0;
        for (Map.Entry runEntry : runs.entrySet()) {
            time += runEntry.getValue () * runEntry.getKey().getTime ();
            //add pauses
            time += this.pause * runEntry.getValue ();
        }
        return time;
    }

    private int getTotalDistance (Map runs) {
        int distance = 0;
        for (Map.Entry 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 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);
        }
    }

}

6
задан DPM 13 March 2012 в 18:17
поделиться