给定是一个N * N网格。现在我们需要找到一条最大长度的好路径,其中好的路径定义如下:
现在给出这几个条件,我需要找出可以进行的最大路径的长度。此外,我需要计算最大长度的路径。
示例:设N = 3,我们有3 * 3矩阵如下:
0 3 2
3 0 1
2 1 0
此处最大良好路径长度为3,此类良好路径的数量为4。
0 3 2
3 0 1
2 1 0
0 3 2
3 0 1
2 1 0
0 3 2
3 0 1
2 1 0
0 3 2
3 0 1
2 1 0
答案 0 :(得分:4)
此问题是Longest Path Problem的变体,但是您的限制会使此问题变得更加容易,因为图表实际上是Directed Acyclic Graph (DAG),因此问题可以有效解决。
定义有向图G=(V,E)
如下:
V = { all cells in the matrix}
(完整性检查:| V | = N ^ 2)E = { (u,v) | u is adjacent to v AND value(u) + 1 = value(v) }
请注意,上述定义中的结果图是一个DAG,因为您不能有任何周期,因为它会产生一些e= (u,v)
的边value(u) > value(v)
。
现在,您只需要从任何起点找到longest path in a DAG。这是通过图表上的topological sort完成的,然后使用动态编程:
init:
for every source in the DAG:
D(v) = 0 if value(v) = 0
-infinity otherwise
step:
for each node v from first to last (according to topological sort)
D(v) = max{D(u) + 1 | for each edge (u,v) }
完成后,找到具有最大值v
的节点D(v)
,这是最长"良好路径"的长度。
找到路径本身是通过重新滚动上面的步骤来完成的,从最大D(v)回溯你的步骤,直到你返回值为0的初始节点。
此方法的复杂性为O(V+E) = O(n^2)
由于您要查找最长路径的数量,因此可以稍微修改此解决方案以计算到达每个节点的路径数,如下所示:
Topological sort the nodes, let the sorted array be arr (1)
For each node v from start to end of arr:
if value(v) = 0:
set D(v) = 1
else
sum = 0
for each u such that (u,v) is an edge: (2)
sum = sum + D(u)
D(v) = sum
以上将为每个节点v
找到"良好路径的数量"到达它的D(v)
。您现在要做的就是找到具有总和节点x
的最大值v
,以便value(v) = x
和D(v) > 0
,并将到达任何节点的路径数相加value(v)
:
max = 0
numPaths = 0
for each node v:
if value(v) == max:
numPaths = numPaths + D(v)
else if value(v) > max AND D(v) > 0:
numPaths = D(v)
max = value(v)
return numPaths
注意:
(1) - a"常规" sort在这里工作,但它需要O(n ^ 2logn)时间,并且拓扑排序需要O(n ^ 2)时间
(2)提醒,(u,v)是边缘,如果:(1)u
和v
相邻(2)值(u)+ 1 =值(v)< / p>
答案 1 :(得分:0)
您可以使用简单的广度优先搜索来完成此操作。
首先找到标记为0的所有细胞。(这是O(N 2 )。)在每个这样的细胞上放一个步行者。每个步行者都会将一个数字'p'初始化为1。
现在迭代:
所有步行者站在具有相同数字k的细胞上。每个助行器都会查找标有k + 1的相邻单元格(左,右,上或下)。
如果没有助行者看到这样的单元格,搜索结束。最长路径的长度为k,这些路径的数量是所有步行者的p的总和。
如果一些步行者看到这样的数字,那就杀死任何不这样做的步行者。
每个步行者都会进入一个好的相邻小区。如果一个步行者看到一个以上的好细胞,它就会分成尽可能多的步行者,就像有好的细胞一样,一个进入每个细胞。 (每个“孩子”具有与其“父母”相同的p
值。)如果两个或更多个步行者在同一个单元格中相遇(即,如果多个路径通向该单元格),则它们组合成一个步行者,'p'值是'p'值的总和。
该算法为O(N 2 ),因为不能多次访问一个单元格,并且步行者的数量不能超过单元格数。
答案 2 :(得分:0)
我是使用ActionScript完成的,希望它具有可读性。我认为它工作正常,但我可能错过了一些东西。
const N:int = 9; // field size
const MIN_VALUE:int = 0; // start value
var field:Array = [];
// create field - not relevant to the task
var probabilities:Array = [0,1,2,3,4,5];
for (var i:int = 0; i < N * N; i++) field.push(probabilities[int(Math.random() * probabilities.length)]);//RANGE));
print_field();
// initial chain fill. We will find any chains of adjacent 0-1 elements.
var chain_list:Array = [];
for (var offset:int = 0; offset < N * N - 1; offset++) {
if (offset < N * N - N) { // y coordinate is not the lowest
var chain:Array = find_chain(offset, offset + N, MIN_VALUE);
if (chain) chain_list.push(chain);
}
if ((offset % N) < N - 1) { // x coordinate is not the rightmost
chain = find_chain(offset, offset + 1, MIN_VALUE);
if (chain) chain_list.push(chain);
}
}
var merged_chain_list:Array = chain_list;
var current_value:int = MIN_VALUE + 1;
// for each found chain, scan its higher end for more attached chains
// and merge them into new chain if found
while(chain_list.length) {
chain_list = [];
for (i = 0; i < merged_chain_list.length; i++) {
chain = merged_chain_list[i];
offset = chain[chain.length - 1];
if (offset < N * N - N) {
var tmp:Array = find_chain(offset, offset + N, current_value);
if (tmp) chain_list.push(merge_chains(chain, tmp));
}
if (offset > N) {
tmp = find_chain(offset, offset - N, current_value);
if (tmp) chain_list.push(merge_chains(chain, tmp));
}
if ((offset % N) < N - 1) {
tmp = find_chain(offset, offset + 1, current_value);
if (tmp) chain_list.push(merge_chains(chain, tmp));
}
if (offset % N) {
tmp = find_chain(offset, offset - 1, current_value);
if (tmp) chain_list.push(merge_chains(chain, tmp));
}
}
//save the last merged result if any and try the next value
if (chain_list.length) {
merged_chain_list = chain_list;
current_value++;
}
}
// final merged list is a list of chains of a same maximum length
print_chains(merged_chain_list);
function find_chain(offset1, offset2, current_value):Array {
// returns always sorted sorted from min to max
var v1:int = field[offset1];
var v2:int = field[offset2];
if (v1 == current_value && v2 == current_value + 1) return [offset1, offset2];
if (v2 == current_value && v1 == current_value + 1) return [offset2, offset1];
return null;
}
function merge_chains(chain1:Array, chain2:Array):Array {
var tmp:Array = [];
for (var i:int = 0; i < chain1.length; i++) tmp.push(chain1[i]);
tmp.push(chain2[1]);
return tmp;
}
function print_field():void {
for (var pos_y:int = 0; pos_y < N; pos_y++) {
var offset:int = pos_y * N;
var s:String = "";
for (var pos_x:int = 0; pos_x < N; pos_x++) {
var v:int = field[offset++];
if (v == 0) s += "[0]"; else s += " " + v + " ";
}
trace(s);
}
}
function print_chains(chain_list):void {
var cl:int = chain_list.length;
trace("\nchains found: " + cl);
if (cl) trace("chain length: " + chain_list[0].length);
for (var i:int = 0; i < cl; i++) {
var chain:Array = chain_list[i];
var s:String = "";
for (var j:int = 0; j < chain.length; j++) s += chain[j] + ":" + field[chain[j]] + " ";
trace(s);
}
}
示例输出:
1 2 1 3 2 2 3 2 4
4 3 1 2 2 2 [0][0] 1
[0][0] 1 2 4 [0] 3 3 1
[0][0] 5 4 1 1 [0][0] 1
2 2 3 4 3 2 [0] 1 5
4 [0] 3 [0] 3 1 4 3 1
1 2 2 3 5 3 3 3 2
3 4 2 1 2 4 4 4 5
4 2 1 2 2 3 4 5 [0]
chains found: 2
chain length: 5
23:0 32:1 41:2 40:3 39:4
33:0 32:1 41:2 40:3 39:4
答案 3 :(得分:0)
我用自己的Lisp方言实现了它,所以源代码不会对你有多大帮助:-) ...
编辑:也添加了Python版本。
无论如何,这个想法是:
paths(i, j) --> (maxlen, number)
,它返回从(i, j)
开始的最大路径长度以及它们中有多少... (i, j)
的{{1}}的邻居将调用M[i][j]+1
以获取有效邻居的结果paths(ni, nj)
,首先检查缓存,否则调用paths
;处理邻居时compute-paths
调用compute-paths
。递归调用的缓存大致相当于一种显式的动态编程方法,但有时更容易实现。要计算最终结果,您基本上会执行相同的计算,但会为所有paths
单元格添加结果,而不是考虑邻居。
请注意,不同路径的数量可能会变得很大,这就是为什么枚举所有这些路径不是一个可行的选项,并且缓存/ DP是必须的:例如对于具有值的0
矩阵N=20
有35,345,263,800条长度为38的最大路径。
该算法及时为O(N ^ 2)(每个单元最多访问一次)并且需要O(N ^ 2)空间用于高速缓存和递归。当然,鉴于输入本身由N ^ 2个数字组成,您至少需要读取它们才能计算出答案,所以你不能指望得到更好的东西。
M[i][j] = i+j
以下是Python中的上述音译,如果您之前从未见过Lisp,那么应该更容易理解......
(defun good-paths (matrix)
(let** ((N (length matrix))
(cache (make-array (list N N)))
(#'compute-paths (i j)
(let ((res (list 0 1))
(count (1+ (aref matrix i j))))
(dolist ((ii jj) (list (list (1+ i) j) (list (1- i) j)
(list i (1+ j)) (list i (1- j))))
(when (and (< -1 ii N) (< -1 jj N)
(= (aref matrix ii jj) count))
(let (((maxlen num) (paths ii jj)))
(incf maxlen)
(cond
((< (first res) maxlen)
(setf res (list maxlen num)))
((= (first res) maxlen)
(incf (second res) num))))))
res))
(#'paths (i j)
(first (or (aref cache i j)
(setf (aref cache i j)
(list (compute-paths i j))))))
(res (list 0 0)))
(dotimes (i N)
(dotimes (j N)
(when (= (aref matrix i j) 0)
(let (((maxlen num) (paths i j)))
(cond
((< (first res) maxlen)
(setf res (list maxlen num)))
((= (first res) maxlen)
(incf (second res) num)))))))
res))