-3

This is from ACM ICPC Japan. The question along with diagram is explained in link here.

Can anyone help me with the solution logic? I know only the naive brute force approach which is not passing me here.

Prem Anand
  • 97
  • 8

2 Answers2

0

Brute force indeed appears to be the solution: http://mainly-coding.blogspot.dk/2010/09/m-judge-10156-kaeru-jump.html . You'll just have to optimize your solution further.

You can do n2^n with dynamic programming, but that uses too much memory.

It is also possible that there are faster algorithms than that, since hammilton path on cubic graphs can besolved in something like 1.25^n, according to wikipedia.

I don't think it's a good problem, since its far from obvious that brute force is faster than 3^30, yet many people will try it and succeed. It's possible that you can prove, that most of the time you'll only have one option for your next jump, and so the running time is way below 3^n, however I don't know how to do that.

Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
0

Below is a brute force solution in Javascript. You can run it directly from the snippet. With the 10x10 sample grid having 30 leaves, the algorithm will visit 11 683 nodes of the search-tree. Or with a different order of looking at the possible jumps, I got 26 269 visits. Like @ThomasAhle anticipated, this is way below 330 (205 891 132 094 649).

A way to further improve would be to detect leaves becoming unreachable, which could then cause an earlier backtrack. The same if two leaves become a one-way trip: this cannot be resolved and so the algorithm could backtrack as soon as it detects this situation.

I did not attempt any of these optimisations as the implementation below finds the solution for the 10x10 sample in only 0.1 seconds on my extremely slow laptop running Firefox.

var directionLetters = 'URDL';

function parseInput(input) {
    // Get input elements and return error message when not valid
    var sp = input.trim().split(/\s+/g);
    var h = parseInt(sp.shift());
    var w = parseInt(sp.shift());
    if (isNaN(w) || isNaN(h)) return "Invalid width or height";
    var grid = sp.join('');
    if (grid.length != w*h) return "Wrong amount of cells";
    var badChars = grid.replace(/[\.oURDL]/g, '');
    if (badChars.length) return "Bad cell characters ('" + badChars + "')";
    
    // Turn grid into double linked list of leaves, in horizontal and vertical
    // directions.
    var count = 0;
    var up = [];
    for (var col = 0; col < w; col++) {
        up[col] = null;
    }
    for (var line = 0; line < h; line++) {
        var left = null;
        for (var col = 0; col < w; col++) {
            var ch = grid[line*w+col];
            if (ch !== '.') {
                leaf = {
                    row: line, // not really needed
                    col: col,  // not really needed
                    neighbor: [
                        up[col],
                        null,
                        null,
                        left
                    ]
                };
                if (left) {
                    left.neighbor[1] = leaf;
                }
                if (up[col]) {
                    up[col].neighbor[2] = leaf;
                }
                left = leaf;
                up[col] = leaf;
                if (directionLetters.indexOf(ch) !== -1) {
                    direction = directionLetters.indexOf(ch);
                    currentLeaf = leaf;
                }
                count++;
            }
        }
    }
    return {
        count: count,
        currentLeaf: currentLeaf,
        direction: direction
    };
}

function getValidJumps(leaves) {
    var jumps = [];
    for (var direction = 0; direction < 4; direction++) { // four directions
        if ((direction ^ leaves.direction) !== 2 // cannot be opposite
                && leaves.currentLeaf.neighbor[direction]) {
            // valid jump
            jumps.push({
                count: leaves.count - 1,
                currentLeaf: leaves.currentLeaf.neighbor[direction],
                direction: direction
            });
        }
    }
    return jumps;
}

function removeLeaf(leaf) {
    // adapt double linked lists to exclude this leaf
    for (var i = 0; i < 4; i++) {
        if (leaf.neighbor[i]) {
            leaf.neighbor[i].neighbor[i ^ 2] = leaf.neighbor[i ^ 2];
        }
    }
}

function restoreLeaf(leaf) {
    // adapt double linked lists to include this leaf
    for (var i = 0; i < 4; i++) {
        if (leaf.neighbor[i]) {
            leaf.neighbor[i].neighbor[i ^ 2] = leaf;
        }
    }
}

// Main recursive algorithm:
function searchPath(leaves) {
    if (leaves.count <= 1) return ''; // found a solution
    var jumps = getValidJumps(leaves);
    removeLeaf(leaves.currentLeaf);
    for (var j = 0; j < jumps.length; j++) {
        var path = searchPath(jumps[j]);
        if (path !== false) return directionLetters[jumps[j].direction] + path;
    };
    restoreLeaf(leaves.currentLeaf);
    return false; // no solution
}

// I/O
document.getElementById('solve').onclick = function() {
    var leaves = parseInput(document.getElementById('input').value);
    document.getElementById('output').textContent = 
        typeof leaves == "string" // error in input?
            ? leaves : searchPath(leaves);
}

document.getElementById('input').oninput = function() {
    document.getElementById('output').textContent = ''; // clear
}
<textarea id="input" rows=11 cols=20 style="float:left">
10 10
.o....o...
o.oo......
..oo..oo..
..o.......
..oo..oo..
..o...o.o.
o..U.o....
oo......oo
oo........
oo..oo....
</textarea>
<button id="solve">Solve</button>
<h4>Solution:</h4>
<div id="output"></div>
trincot
  • 317,000
  • 35
  • 244
  • 286