求两点之间最短路径,个人平常用的最多的是floyd算法,算法相当简洁,写起来也方便,但是这里的格点比较多,不适合floyd,而对于dijkstra算法,当树接节点占满图后,直接退化成floyd算法,时间复杂度也变得不是很理想,这里用A* (求路径有各种各样的算法,有A*的变种,也有些对于实时度较高的甚至采用蚁群算法,这是个很复杂的领域,无人驾驶,导航,路由,加速器,都用到路径规划,但针对某一特定问题选择的算法都会不同。):
var cutOffTree = function(forest) {
var ans = 0;
//floyd
var trees = [];
for(let i = 0; i < forest.length; i++) {
for(let j = 0; j < forest[i].length; j++) {
if(forest[i][j] > 1) {
trees.push([forest[i][j],[i,j]]);
}
}
}
trees.sort((a, b)=>{
return a[0] - b[0];
});
trees.unshift([1,[0,0]]);
//A*,floyd(时间复杂度是最高的),dijkstra(居中),bfs(时间复杂度过高)
//2500个树节点会退化到O(n^3)
//采用A*
const A_STAR = (sourceX, sourceY, targetX, targetY) => {
let CLOSED = [];
let OPEN = [];
let MAP_CLOSED = [];
let MAP_OPEN = [];
const calc_h = (curX,curY, targetX, targetY) => {
return Math.abs(curX - targetX) + Math.abs(curY - targetY);
}
OPEN.push([0,[sourceX, sourceY, 0]]);
MAP_OPEN[sourceX] = MAP_OPEN[sourceX] ? MAP_OPEN[sourceX] : [];
MAP_OPEN[sourceX][sourceY] = 1;
while(true) {
if(OPEN.length == 0) {
return -1;
} else {
let prepare_pos = 0;
for(let i = 1; i < OPEN.length; i++) {
if(OPEN[i][0] == OPEN[prepare_pos][0]) {
if(OPEN[i][1][0] == targetX && OPEN[i][1][1] == targetY) {
prepare_pos = i;break;
}
} else {
break;
}
}
let cur_node = JSON.parse(JSON.stringify(OPEN.splice(prepare_pos, 1)[0]));
CLOSED.push(JSON.parse(JSON.stringify(cur_node)));
cur_node = cur_node[1];
MAP_CLOSED[cur_node[0]] = MAP_CLOSED[cur_node[0]] ? MAP_CLOSED[cur_node[0]] : [];
MAP_CLOSED[cur_node[0]][cur_node[1]] = 1;
MAP_OPEN[cur_node[0]][cur_node[1]] = 0;
if(cur_node[0] == targetX && cur_node[1] == targetY) {
//console.log(cur_node);
return cur_node[2];
}
let son_node = [];
if(cur_node[0] - 1 >= 0 && forest[cur_node[0] - 1][cur_node[1]] > 0) {
son_node.push([cur_node[0] - 1, cur_node[1]]);
}
if(cur_node[0] + 1 < forest.length && forest[cur_node[0] + 1][cur_node[1]] > 0) {
son_node.push([cur_node[0] + 1, cur_node[1]]);
}
if(cur_node[1] - 1 >= 0 && forest[cur_node[0]][cur_node[1]-1] > 0) {
son_node.push([cur_node[0], cur_node[1]-1]);
}
if(cur_node[1] + 1 < forest[0].length && forest[cur_node[0]][cur_node[1]+1] > 0) {
son_node.push([cur_node[0], cur_node[1]+1]);
}
for(let i = 0; i < son_node.length; i++) {
let h = cur_node[2] + 1 + calc_h(son_node[i][0],son_node[i][1],targetX,targetY);
if(
(MAP_CLOSED[son_node[i][0]] == undefined || MAP_CLOSED[son_node[i][0]][son_node[i][1]] == undefined || !MAP_CLOSED[son_node[i][0]][son_node[i][1]])
&&
(MAP_OPEN[son_node[i][0]] == undefined || MAP_OPEN[son_node[i][0]][son_node[i][1]] == undefined || !MAP_OPEN[son_node[i][0]][son_node[i][1]])
) {
OPEN.unshift([h,[son_node[i][0],son_node[i][1],cur_node[2] + 1]]);
OPEN.sort((a,b) => { return a[0] - b[0]; });
MAP_OPEN[son_node[i][0]] = MAP_OPEN[son_node[i][0]] ? MAP_OPEN[son_node[i][0]] : [];
MAP_OPEN[son_node[i][0]][son_node[i][1]] = 1;
} else {
if(MAP_OPEN[son_node[i][0]][son_node[i][1]]) {
for(let j = 0; j < OPEN.length; j++) {
if(OPEN[j][1][0] == son_node[i][0] && OPEN[j][1][1] == son_node[i][1]) {
if(h < OPEN[j][0]) {
OPEN[j][0] = h; OPEN[j][1][2] = cur_node[2] + 1;
OPEN.sort((a,b)=>{ return a[0] - b[0];});
}
break;
}
}
} else if(MAP_CLOSED[son_node[i][0]][son_node[i][1]]) {
for(let j = 0; j < CLOSED.length; j++) {
if(CLOSED[j][1][0] == son_node[i][0] && CLOSED[j][1][1] == son_node[i][1]) {
if(h < CLOSED[j][0]) {
let t_node = CLOSED.splice(j,1)[0];
MAP_CLOSED[t_node[1][0]][t_node[1][1]] = 0;
t_node[0] = h; t_node[1][2] = cur_node[2] + 1;
OPEN.unshift(JSON.parse(JSON.stringify(t_node)));
MAP_OPEN[t_node[1][0]][t_node[1][1]] = 1;
OPEN.sort((a,b)=>{ return a[0] - b[0];});
}
break;
}
}
}
}
}
}
}
}
for(let i = 1; i < trees.length; i++) {
let dis = A_STAR(trees[i-1][1][0], trees[i-1][1][1], trees[i][1][0], trees[i][1][1]);
if(dis == -1) {
return -1;
}
/*console.log("target:" + trees[i-1][1]);
console.log("source:" + trees[i][1]);
console.log("step:" + dis);
**/
ans += dis;
}
return ans;
};