例如:
假设这些点是:1 5 10 20 40
假设这些权值是:1 2 10 50 13
如果我选择从点10开始,然后移动到点20、然后到5、然后到40,最后到1,那么加权和将变成10*0+50*(10)+2*(10+15)+13*(10+15+35)+1*(10+15+35+39)。
我尝试使用贪心算法来解决这个问题,从具有最大相关权重的点开始,移动到第二大的权重点,以此类推。但是算法不起作用。请问有人可以给我指点一下该如何解决这个问题吗?
有一个非常重要的事实可以导致多项式时间算法:
由于点位于某些轴上,它们生成了一条路径图,这意味着对于每个三个顶点v1,v2,v3
,如果v2
在v1
和v3
之间,则v1
和v3
之间的距离等于v1
和v2
之间的距离加上v2
和v3
之间的距离。因此,例如,如果我们从v1
开始,则首先到v2
,然后到v3
的路径的目标函数值始终小于首先到v3
,然后返回v2
的路径的值,因为:
第一条路径的值= w [2] * D(v1,v2)+W [3] *(D(v1,v2)+D(v2,v3))
第二条路径的值= w [3] * D(v1,v3)+W [2] *((v1,v3)+D(v3,v2))= w [3] * D(v1,v2)+w [3] * D(v2,v3)+w [2] *(D(v1,v2)+2 * D(v3,v2))
如果我们从第一条路径值中减去第二条路径值,我们得到w [2] * 2 * D(v3,v2)
,除非您考虑负权重,否则它等于或大于0。
所有这些意味着,如果我们位于某个点,我们应该始终只考虑两个选项:向左或向右前往最近的未访问点。
这非常重要,因为它使我们只剩下2 ^ n
个可能的路径,而不是n!
个可能的路径(就像旅行商问题中一样)。
使用动态规划可以在路径图上解决TSP /最小权重哈密顿路径,您应该应用完全相同的方法,但修改计算目标函数的方式。
由于您不知道起始顶点,因此您将不得不从不同的顶点开始运行此算法n
次,这意味着运行时间将乘以n
。
n
个阶段,在每个阶段中,我们填充一个具有n
列和(|V| choose n-1)
行的表。在路径图上解决这样的问题时,在每个阶段中,我们只有2列和1行,因为如果到达某个状态时,我们已经访问了这些点中的唯一可能的顶点子集。在填充表格时,我们可以使用上述目标函数而不是TSP的目标函数。 - Ron Tellerimport java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MinWeightSum
{
public static void main(String[] args)
{
double x[] = { 1, 5, 10, 20, 40 };
double w[] = { 1, 2, 10, 50, 13 };
List<Integer> givenIndices = Arrays.asList(2, 3, 1, 4, 0);
Path path = createPath(x, w, givenIndices);
System.out.println("Initial result "+path.sum);
List<Integer> sortedWeightIndices =
computeSortedWeightIndices(w);
Path greedyPath = createPath(x, w, sortedWeightIndices);
System.out.println("Greedy result "+greedyPath.sum);
System.out.println("For "+sortedWeightIndices+" sum "+greedyPath.sum);
}
private static Path createPath(
double x[], double w[], List<Integer> indices)
{
Path path = new Path(x, w);
for (Integer i : indices)
{
path.append(i);
}
return path;
}
private static List<Integer> computeSortedWeightIndices(final double w[])
{
List<Integer> indices = new ArrayList<Integer>();
for (int i=0; i<w.length; i++)
{
indices.add(i);
}
Collections.sort(indices, new Comparator<Integer>()
{
@Override
public int compare(Integer i0, Integer i1)
{
return Double.compare(w[i1], w[i0]);
}
});
return indices;
}
static class Path
{
double x[];
double w[];
int prevIndex = -1;
double distance;
double sum;
Path(double x[], double w[])
{
this.x = x;
this.w = w;
}
void append(int index)
{
if (prevIndex != -1)
{
distance += Math.abs(x[prevIndex]-x[index]);
}
sum += w[index] * distance;
prevIndex = index;
}
}
}
您在示例中描述的索引序列产生了该解决方案。
For [2, 3, 1, 4, 0] sum 1429.0
你所描述的贪心算法会得到
For [3, 4, 2, 1, 0] sum 929.0
最佳解决方案是
For [3, 2, 4, 1, 0] sum 849.0
我通过检查所有索引的排列方式找到了这个(当然,对于较大的n
来说这是不可行的)
因此,一种昂贵但多项式的动态规划方法的状态包括当前位置(必须是其中一个点),当前位置左侧第一个未访问点的位置(如果有的话),以及当前位置右侧第一个未访问点的位置(如果有的话)。我们最多应该考虑访问下一个点的两个位置 - 当前点右侧的点和当前点左侧的点。我们可以通过查看剩余点数更少的状态的预计算成本来计算这两种选择的成本,并将最佳结果存储为从此点开始的最佳可能成本。我们可以在到达当前点时假设D=0来计算这些成本。当我们查找存储的成本时,它们也是在这个假设下存储的(但是在它们的当前点处D=0,而不是我们的当前点),但是我们知道在那个阶段剩下的点的权重之和,因此我们可以添加存储成本和权重之和乘以我们当前点与我们正在查找成本的点之间的距离来补偿这一点。
这将会产生O(n^3)的成本,因为您正在构建一个具有O(n^3)个单元格的表格,每个单元格都是相对简单的过程的乘积。然而,因为没有访问到的单元格永远没有意义,当前点必须在区间两端的其中一个点旁边,因此我们只需要考虑O(n^2)种可能性,这将把成本降低到O(n^2)。例如,像(0,1,-1,2,-2,3,-3,4,-4…)这样的锯齿路径可能是适当奇异权重的最佳解决方案,但即使是从-2到3这样的情况,-2也是两个点3和-3之间尚未采取的最近点。
我在http://www.mcdowella.demon.co.uk/Plumber.java中放了一个Java实现。测试工具针对长度为12及以下的多个随机生成的测试用例检查此DP版本与(慢)几乎穷举的版本。它可能仍然不完全没有漏洞,但希望它能填补细节。
|E|<=2|V|
的TSP问题简化为这个问题... - amit