我正在编写一个编程课程的问题,我在开发适合该问题的算法时遇到问题。这是:
你要去长途旅行。你从0英里处的道路开始。沿途有n 酒店,在英里的帖子a1< a2< ......< a,其中每个ai从起点开始测量。该 只允许您停在这些酒店的地方,但您可以选择哪家酒店 你停下来。您必须在最终的酒店(距离a)停留,这是您的目的地。 理想情况下,您希望每天行驶200英里,但这可能无法实现(取决于间距 的酒店)。如果您在一天内行驶x英里,则当天的罚款为(200 - x)^ 2。你要 计划你的旅行,以尽量减少总罚款,即所有旅行天数的总和 每日处罚。 提供一种有效的算法,确定要停止的酒店的最佳顺序。
所以,我的直觉告诉我从后面开始,检查惩罚值,然后以某种方式匹配它们返回前进方向(导致O(n ^ 2)运行时,这对于情况来说是最佳的。)< / p>
任何人都认为有任何可能的方法可以解决这个想法,或者对可能存在的意见有任何想法吗?
答案 0 :(得分:9)
如果x
是标记号,ax
是该标记的里程数,px
是到达该标记的最低罚分,您可以计算pn
如果您在n
之前了解pm
所有标记m
,则可以使用标记n
。
要计算pn
,请找出pm + (200 - (an - am))^2
和m
小于{{1}当前最佳标记am < an
的所有标记(200 - (an - am))^2
的最小值pn
(最后一部分是优化)。
对于标记0
,a0 = 0
的起始标记p0 = 0
,1
和p1 = (200 - a1)^2
。根据该起始信息,您可以计算p2
,然后p3
等,直至pn
。
编辑:使用OP评论中的示例切换到Java代码。请注意,这没有第二段中描述的优化检查。
public static void printPath(int path[], int i) {
if (i == 0) return;
printPath(path, path[i]);
System.out.print(i + " ");
}
public static void main(String args[]) {
int hotelList[] = {0, 200, 400, 600, 601};
int penalties[] = {0, (int)Math.pow(200 - hotelList[1], 2), -1, -1, -1};
int path[] = {0, 0, -1, -1, -1};
for (int i = 2; i <= hotelList.length - 1; i++) {
for(int j = 0; j < i; j++){
int tempPen = (int)(penalties[j] + Math.pow(200 - (hotelList[i] - hotelList[j]), 2));
if(penalties[i] == -1 || tempPen < penalties[i]){
penalties[i] = tempPen;
path[i] = j;
}
}
}
for (int i = 1; i < hotelList.length; i++) {
System.out.print("Hotel: " + hotelList[i] + ", penalty: " + penalties[i] + ", path: ");
printPath(path, i);
System.out.println();
}
}
输出是:
Hotel: 200, penalty: 0, path: 1
Hotel: 400, penalty: 0, path: 1 2
Hotel: 600, penalty: 0, path: 1 2 3
Hotel: 601, penalty: 1, path: 1 2 4
答案 1 :(得分:6)
您似乎可以使用动态编程解决此问题。子问题如下:
d(i)
:从一开始到酒店旅行的最低罚款i
。
递归公式如下:
d(0) = 0
其中0是起始位置。
d(i) = min_{j=0, 1, ... , i-1} ( d(j) + (200-(ai-aj))^2)
到达酒店i
的最低罚款是通过尝试前一天的所有停靠位置,添加今天的罚款并采取最低罚款。
为了找到路径,我们存储在一个单独的数组(路径[])中,我们必须从哪个酒店出发,以达到该特定酒店的最低罚款。通过向后遍历数组(从路径[n]),我们获得路径。
运行时为O(n ^ 2)。
答案 2 :(得分:3)
这相当于在方向非循环图中找到两个节点之间的最短路径。 Dijkstra的算法将在O(n ^ 2)时间运行。
但是,你的直觉更好。从后面开始,计算在该酒店停车的最低罚款。第一家酒店的罚款只是(200-(200-x)^ 2)^ 2。然后,对于其他每家酒店(按相反顺序),向前扫描以找到最低罚款的酒店。一旦惩罚成本开始增加,就会停止一个简单的优化,因为这意味着你已经超过全局最低值。答案 3 :(得分:1)
我认为你不能像sysrqb那样容易地做到这一点。
另一方面,从开始或结束开始确实没有区别;我们的目标是找到每条路的最小停靠量,每个站点尽可能接近200米。
如上所述的问题似乎允许每天超过200米,并且惩罚同样适用于超过或低于(因为它是平方的)。由于惩罚相等,但是目标更接近,这更倾向于每天超过英里而不是未成年人。
但是,考虑到这种布局
A ----- B----C-------D------N
0 190 210 390 590
并非总是如此。最好转到B-> D-> N,总罚分仅为(200-190)^2 = 100
。通过C-> D-> N进一步给出100+400=500
的惩罚。
如果您已经有一个达到P点的最佳解决方案,那么答案看起来就像是一个完全广泛的首次搜索和主动修剪,到目前为止删除了所有解决方案
sum(penalty-x) > sum(penalty-p) AND distance-to-x <= distance-to-p - 200
这将是一个O(n ^ 2)算法
<小时/> 类似......
答案 4 :(得分:1)
以下是酒店问题的MATLAB代码。
clc
clear all
% Data
% a = [0;50;180;150;50;40];
% a = [0, 200, 400, 600, 601];
a = [0,10,180,350,450,600];
% a = [0,1,2,3,201,202,203,403];
n = length(a);
opt(1) = 0;
prev(1)= 1;
for i=2:n
opt(i) =Inf;
for j = 1:i-1
if(opt(i)>(opt(j)+ (200-a(i)+a(j))^2))
opt(i)= opt(j)+ (200-a(i)+a(j))^2;
prev(i) = j;
end
end
S(i) = opt(i);
end
k = 1;
i = n;
sol(1) = n;
while(i>1)
k = k+1;
sol(k)=prev(i);
i = prev(i);
end
for i =k:-1:1
stops(i) = sol(i);
end
stops
答案 5 :(得分:1)
第1步(共2步)
子问题:
在这种情况下,当“0 <= i <= n”时,“C(j)”被认为是直到酒店“ai”获得的最小罚分的子问题。问题所需的值是“C(n)”。
查找最小总惩罚的算法:
如果行程在“aj”位置停止,那么前一个停止将是“ai”,而i的值应该小于j。然后,“ai”的所有可能性如下:
C(j)min {C(i)+(200-(aj-ai))^ 2},0 <= i <= j。
将“C(0)”的值初始化为“0”,将“a0”初始化为“0”以找到剩余的值。
要找到最佳路线,请为每次迭代增加“j”和“i”的值,并使用此详细信息从“C(n)”回溯。
这里,“C(n)”是指最后一家酒店的罚款(即“i”的值介于“0”和“n”之间。
伪代码:
//功能定义
程序min_tot()
//外部循环表示j = 1到n的值:
//计算每个站点的距离C(j)=(200-aj)^ 2
//内部循环表示i = 1到j-1的值:
//计算总惩罚并指定最小//总惩罚 “C(j)的”
C(j)= min(C(i),C(i)+(200 - (aj-ai))^ 2}
//返回上一家酒店的总罚款值
返回C(n)
第2步(共2步)
说明: 上述算法用于找出从起点到终点的最小总罚分。
算法的运行时间:
该算法包含“n”个子问题,每个子问题需要“O(n)”次才能解决。
只需要计算“O(n)”的最小值。
回溯过程需要“O(n)”次。
算法的总运行时间为nxn = n ^ 2 = O(n ^ 2)。
因此,该算法完全采用“0(n ^ 2)”次来解决整个问题。
答案 6 :(得分:0)
为了简洁地回答你的问题,对于大多数约束满足问题,PSPACE完整算法通常被认为是“有效的”,所以如果你有一个O(n ^ 2)算法,那就是“有效”。
我认为最简单的方法,即每天N英里和200英里,将N除以200得到X;你旅行的天数。将其舍入到最接近的整数天X',然后将N除以X'得到Y,即一天中行驶的最佳里程数。这实际上是一个恒定时间操作。如果每隔Y英里有一家酒店,那么在这些酒店停车会产生尽可能低的分数,最大限度地减少每天罚款的影响。例如,如果总行程为605英里,则每天行驶201英里(最后一次为202)的罚款为1 + 1 + 4 = 6,远低于0 + 0 + 25 = 25(200 + 200 + 205)你去的时候最大限度地减少每一天的旅行罚款。
现在,您可以遍历酒店列表。最快的方法是简单地选择最接近Y英里每个倍数的酒店。这是线性时间,会产生“好”的结果。但是,我不认为这会在所有情况下产生“最佳”结果。
更复杂但更简单的方法是将两个最接近的酒店分配到Y的每个倍数;紧接着之前的那个和紧接着之后的那个。这产生了一对X'对,可以在2 ^ X'时间内以所有可能的排列遍历。您可以通过将Dijkstra应用于这些对的地图来缩短这一点,这将确定每天旅行中成本最低的路径,并将在大约(2X')^ 2时间内执行。这可能是保证产生最佳结果的最有效算法。
答案 7 :(得分:0)
正如@rmmh所说,你找到了最小距离路径。这里距离是罚分(200-x)^ 2
因此,您将尝试通过查找最低罚款来找到停止计划。
让我们说D(ai)给出起点的距离
P(i)= min {P(j)+(200 - (D(ai) - D(dj))^ 2}其中j为:0 <= j 的
从偶然的分析来看,它似乎是
O(n ^ 2)算法(= 1 + 2 + 3 + 4 + .... + n)= O(n ^ 2)
答案 8 :(得分:0)
我最近遇到了这个问题,想分享用Java语言编写的解决方案。
与以上大多数解决方案没有什么不同,我使用了动态编程方法。为了计算penalties[i]
,我们需要搜索前一天的停靠地点,以使惩罚最小。
penalties(i) = min_{j=0, 1, ... , i-1} ( penalties(j) + (200-(hotelList[i]-hotelList[j]))^2)
解决方案不假定第一个惩罚是Math.pow(200 - hotelList[1], 2)
。我们不知道停止在第一高位是否是最佳选择,因此不应进行此假设。
为了找到最佳路径并存储沿途的所有停靠点,正在使用辅助数组path
。最后,将数组向后遍历以计算finalPath
。
function calculateOptimalRoute(hotelList) {
const path = [];
const penalties = [];
for (i = 0; i < hotelList.length; i++) {
penalties[i] = Math.pow(200 - hotelList[i], 2)
path[i] = 0
for (j = 0; j < i; j++) {
const temp = penalties[j] + Math.pow((200 - (hotelList[i] - hotelList[j])), 2)
if (temp < penalties[i]) {
penalties[i] = temp;
path[i] = (j + 1);
}
}
}
const finalPath = [];
let index = path.length - 1
while (index >= 0) {
finalPath.unshift(index + 1);
index = path[index] - 1;
}
console.log('min penalty is ', penalties[hotelList.length - 1])
console.log('final path is ', finalPath)
return finalPath;
}
// calculateOptimalRoute([20, 40, 60, 940, 1500])
// Outputs [3, 4, 5]
// calculateOptimalRoute([190, 420, 550, 660, 670])
// Outputs [1, 2, 5]
// calculateOptimalRoute([200, 400, 600, 601])
// Outputs [1, 2, 4]
// calculateOptimalRoute([])
// Outputs []
答案 9 :(得分:0)
作为概念证明,这是我在动态编程中没有嵌套循环的JavaScript解决方案。
我们从零英里处开始。
通过将循环中当前酒店的罚款与先前酒店的罚款进行比较,将罚款保持在最低水平,我们找到了下一站。
一旦我们有了当前的最低要求,我们就会找到当天的止损点。我们将此点指定为下一个起点。
(可选),我们可以保留罚款总数:
public class ComedServelt extends HttpServlet {
...
public void init(ServletConfig config) throws ServletException {
...
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
...
doPost(request, response);
...
}
public void doPost(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws ServletException,IOException {
...
}
public void destroy() {
LogManager.shutdown();
}
}
答案 10 :(得分:0)
这是我使用动态编程的Python解决方案:
distance = [150,180,250,340]
def hotelStop(distance):
n = len(distance)
DP = [0 for _ in distance]
for i in range(n-2,-1,-1):
min_penalty = float("inf")
for j in range(i+1,n):
# going from hotel i to j in first day
x = distance[j]-distance[i]
penalty = (200-x)**2
total_pentalty = penalty+ DP[j]
min_penalty = min(min_penalty,total_pentalty)
DP[i] = min_penalty
return DP[0]
hotelStop(distance)