我试图解决这个问题,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>
答案 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,则执行以下操作:
此时,您再次检查上一个城市。当你可以从每个城市到下一个城市时,你就完成了。你可能最终得到一个无法到达下一个城市的城市;在那种情况下你失败了。
答案 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)时间来检查是否城市可以完全环绕。