动态编程:找到到达目的地的可能方式

时间:2014-06-25 12:13:19

标签: algorithm dynamic-programming

我试图解决这个问题,O(N ^ 2)解决方案很简单,O(N)是可能的,但我想不出怎么做。这是个问题:


史蒂文的世界有N个城市和N个直达道路。城市的编号从0到N-1。史蒂文可以从城市i到城市(i + 1)%N,(0-> 1 - > 2 - > ... - >> N - 1 - > 0)。

史蒂文想开车环游世界。他的汽车油箱的容量是C加仑。他可以在城市i的开头使用[i]加仑,并且汽车需要b [i]加仑从城市i到(i + 1)%N。

史蒂文可以开车多少个城市,以便他可以环游世界并到达他开始的同一个城市?

请注意

油箱最初是空的。

输入格式 第一行包含两个整数(由空格分隔):城市号N和容量C. 第二行包含N个以空格分隔的整数:a [0],a [1],...,a [N-1]。 第三行包含N个以空格分隔的整数:b [0],b [1],...,b [N - 1]。

输出格式 可以选择作为起始城市的城市数量。

示例输入

3 3
3 1 2
2 2 2
样本输出

2
解释

史蒂文从0号城市开始,用3加仑的燃料填充他的汽车,并使用2加仑的燃料前往城市1.他的油箱现在有1加仑的燃料。 在1号城市加油1加仑燃料后,他使用2加仑燃料前往2号城市。他的油箱现在是空的。 在2号城市为2加仑燃料加油后,他使用2加仑燃料返回0号城市。

这是第二种可能的解决方案。 史蒂文从2号城市出发,用2加仑装满他的车,然后前往0号城市。 在从0号城市加油3加仑燃料时,他然后前往1号城市,并排出2加仑的燃料。他的油箱现在含有1加仑的燃油。然后,他可以在1号城市加油1加仑燃料,并将他的汽车燃料增加到2加仑,然后前往2号城市。

然而,史蒂文无法从城市1开始,因为他只获得1加仑的燃料,但前往城市2需要2加仑。

因此答案2。


现在我知道这个算法可以用O(N)时间复杂度来解决,我无法猜测它可以用动态编程来解决,请帮我弄清楚它是如何分解成子问题的。 / p>

5 个答案:

答案 0 :(得分:3)

我已经制作了一个应该解决问题的算法,它为你的情况输出2,但它必须在其他测试用例上测试。 我不确定它是否正确。我的主要想法是,如果你可以从一个点开始迭代,你可以做出你想要的数量,反过来也是如此。如果你做不止一个,你甚至不能做一个。

#include <algorithm>
#include <iostream>

using namespace std;

#define PROB_SIZE 3
int n = PROB_SIZE, c = 3;
int a[PROB_SIZE] = {3, 1, 2}; // available
int b[PROB_SIZE] = {2, 2, 2}; // used
int d[PROB_SIZE];
int dmin[PROB_SIZE];

int main()
{
    //The fuel used in the trip to next node (amount put in minus the amount consumed in one trip).
    for (int i = 0; i < n; i++) {
        d[i] = a[i] - b[i];
    }
    //The fuel that i need to start a trip in this point and reach point 0.
    dmin[n - 1] = d[n - 1];
    for (int i = n - 2; i >= 0; i--) {
        dmin[i] = min(d[i], d[i] + dmin[i + 1]);
    }
    //The second loop to be sure i cover a whole loop from any point.
    dmin[n - 1] = min(d[n - 1], d[n - 1] + dmin[0]);
    for (int i = n - 2; i >= 0; i--) {
        dmin[i] = min(d[i], d[i] + dmin[i + 1]);
    }
    //If for any point i need to have more fuel than i can carry then the trip is impossible for all points.
    for (int i = 0; i < n; i++) {
        if ((-dmin[i] + a[i]) > c) {
            cout << 0 << endl;
            return 0;
        }
    }
    int cnt = 0;
    //Any point that i need to have 0 fuel to reach point 0 making at least one round trip is a good starting point.
    for (int i = 0; i < n; i++) {
        if (dmin[i] >= 0) {
            cnt++;
        }
    }
    cout << cnt << endl;
}

答案 1 :(得分:0)

当你试图找出你是否可以从城市我回到城市i时,你需要收集有关以前城市的信息。我创建了一个堆栈,其中包含您可以从城市x开始的信息,并在坦克中使用z燃料到达城市y。

当您检查城市j时,您发现可以将X燃料放入油箱中的j,而驾驶到j + 1则需要Y燃油。如果X> = Y,则将该信息放在堆栈上。否则,弹出堆栈的顶部。那里的信息会告诉你,你可以从某个x开始并在坦克中使用z燃料到达j。从x开始,你将在罐中留下j(min + z + X,C)。如果这就足够了,请将信息推回​​堆栈。如果没有,请从堆栈中弹出下一个项目。如果堆栈为空,则无法达到j + 1。

现在你需要弄清楚如何结束搜索,并证明只有O(N)操作。

答案 2 :(得分:0)

更简单的方法:您拥有城市列表,并逐个删除无法启动的城市。

你寻找第一个没有足够燃料进入城市i + 1的城市。如果没有这样的城市,你可以从任何地方开始。由于您无法从i到i + 1,因此您将其从城市列表中删除,但您需要将其与之前的城市相结合。如果前一个城市有x燃料且需要y,x> = y,而城市i有X燃料且需要Y,则执行以下操作:

  1. 用最小值替换X(X,C - (x - y))(因为不能使用额外的燃料)。
  2. 从y和X减去min(y,X)(因为那是你可以补充的)
  3. 将x替换为min(C,x + X),将y替换为y + Y.
  4. 此时,您再次检查上一个城市。当你可以从每个城市到下一个城市时,你就完成了。你可能最终得到一个无法到达下一个城市的城市;在那种情况下你失败了。

答案 3 :(得分:0)

static int n = 3;
static int c = 3;
static int a[] = {3, 1, 2}; 
static int b[] = {2, 2, 2}; 
static int currentCity;

public static void main(String[] args) {

    List<String> citi = new ArrayList<String>();

    //try one by one
    for(int i = 0; i < n; i ++){
        currentCity = i;
        if(!startFrom(i, 0))
            continue;

        citi.add("citi" + i);
    }

    for (String s: citi)
        System.out.println(s);
}

public static boolean startFrom(int i, int left){
    int tankVal = (a[i] + left) > c ? c : (a[i] + left);

    if(b[i] > tankVal)
        return false;

    left = tankVal - b[i];

    int next = (i + 1) % n;
    if(next == currentCity)
        return true;
    return startFrom(next, left);
}

答案 4 :(得分:0)

首先,我想指出的是,这个问题是word-for-word lifted from an exercise on HackerRank

这是一个算法的草图,该算法已确认可以在O(N)时间内通过该站点上的所有测试用例

对于从0开始到i等于0

1)为了成功地从0到i,我们从0城市开始旅行所需的最少汽油是多少?

2)从这个最小数量开始(假设甚至可以进行部分旅行),进入城市i时我们将拥有多少汽油?

3)在这样的旅行中,您的油箱中携带的最大汽油量是多少?

我们之所以需要#3的原因是因为有限的油箱容量有时会阻止我们进行某些行程的“气体分布图”,而只是“将所有内容调高”。知道我们在给定行程中离天花板有多近,可以准确地告诉我们在达到天花板之前我们可以“向上移动”多少。 (这听起来有些含糊,但是应该仔细考虑这一点。)

一旦每三个0

可以使用一些稍微聪明的动态编程在每个城市的O(1)时间中计算所有六个品质因数,一旦对所有城市都拥有了它们,则需要O(1)时间来检查是否城市可以完全环绕。