Javascript 检查多组对是否覆盖给定的一组对

Javascript 检查多组对是否覆盖给定的一组对,javascript,arrays,algorithm,data-structures,Javascript,Arrays,Algorithm,Data Structures,假设我们有N对数组,例如对于N=3: A1=[[3,2],[4,1],[5,1],[7,1],[7,2],[7,3] A2=[[3,1],[3,2],[4,1],[4,2],[4,3],[5,3],[7,2] A3=[[4,1],[5,1],[5,2],[7,1]] 我们可以假设每个数组中的对都按第一个数字排序,然后按第二个数字排序。同样,同一对不会在同一数组中出现多次(如上所示,同一对可以出现在多个数组中) 每对中的数字都是大于等于1的任意整数 我如何找到所有满足以下条件的k: (简单地说,

假设我们有
N
对数组,例如对于
N=3

A1=
[[3,2],[4,1],[5,1],[7,1],[7,2],[7,3]

A2=
[[3,1],[3,2],[4,1],[4,2],[4,3],[5,3],[7,2]

A3=
[[4,1],[5,1],[5,2],[7,1]]

我们可以假设每个数组中的对都按第一个数字排序,然后按第二个数字排序。同样,同一对不会在同一数组中出现多次(如上所示,同一对可以出现在多个数组中)

每对中的数字都是大于等于1的任意整数

我如何找到所有满足以下条件的
k

(简单地说,
[k,1],[k,2],…,[k,N]
存在于不同的数组中)

上述示例的预期结果是:
[5,7]

注意:速度是算法最关键的因素,其次是内存。如果有助于优化速度/内存,则假设
N计划

创建一个数据对象(称之为
data
data={}
),如下所示:

对象中的每个关键点都是k候选点,也就是说,它是所有对集合中唯一的第一个坐标值。在上面的示例中,键应该是
[3,4,5,7]
。键的值是一个包含元素1..N的数组,它表示A1..an。这些数组中的每个元素都是该数组中出现的与第一个坐标值匹配的第二个坐标值。对于上面的示例,
data[3][1]=[2]
因为坐标[3,1]∈ A2
数据[3][2]=[1,2]
因为[3,2]∈ A1和A2

从那里,检查每个找到的k值。如果
data[k]
中的数组少于N个,则丢弃它,因为它显然不起作用

从那里,检测P1…Pn是否成立,我的想法是:生成
数据[k]
中数组的所有排列,对于每个排列集AP1…APn(AP=A permuted),查看1是否在AP1中,2是否在AP2中…等等。如果每个n都被找到,我们就找到了一个k!我必须实际编写代码,以确定它是否有效,因此,实际代码如下。我借用了函数来生成置换

    var permArr, usedChars, found;
    //found ks go here
    found = [];

    //needed later
    function permute(input, start) {
        if (start === true) {
            permArr = [];
            usedChars = [];
        }
        var i, ch;
        for (i = 0; i < input.length; i++) {
            ch = input.splice(i, 1)[0];
            usedChars.push(ch);
            if (input.length === 0) {
                permArr.push(usedChars.slice());
            }
            permute(input);
            input.splice(i, 0, ch);
            usedChars.pop();
        }
        return permArr;
    }

    //the main event
    function findK(arrays) {

        //ok, first make an object that looks like this:
        //each key in the object is a k candidate, that is, its every unique first coordinate 
        //value in a pair. the value in array with elements 1..N, which represents A_1..A_n.
        //The elements in that array are each 2nd coordinate value that appears in that array 
        //matched with the first coordinate value, ie, data[3][1] has the array [2] 
        //because coordinate [3,1] is found in A_2.

        data = {};
        var N = arrays.length;
        for (var k = 0; k < N; k++) {
            //0/1 indexing
            var n = k + 1;
            for (var i = 0; i < arrays[k].length; i++) {
                var c1 = arrays[k][i][0];
                var c2 = arrays[k][i][1];
                if (!(c1 in data)) {
                    data[c1] = [];
                }
                if (!(c2 in data[c1])) {
                    data[c1][c2] = [];
                }
                data[c1][c2].push(n);
            }
        }
        data2 = [];


        //next, look at what we have. If any data[k] has less than n elements, disregard it
        //because not all of 1..N is represented. if it does, we need to test that it works,
        //that is, for each n in 1..N, you can find each value in a different array.
        //how do we do that? idea: go through every permutation of the N arrays, then 
        //count up from 1 to n and see if each n is in subarray_n

        //get all permutations
        //make an array from 1 to n
        var arr = [];
        for (var n = 1; n <= N; n++) {
            arr.push(n);
        }
        perms = permute(arr, true);

        for (k in data) {

            if (Object.keys(data[k]).length < N) {
                //not all 3 arrays are represented
                continue;
            }
            else {

                //permute them
                for (i in perms) {
                    if (found.indexOf(k) > -1) {
                        continue;
                    }
                    //permuations
                    //permutated array
                    var permuted = [undefined];
                    for (var j in perms[i]) {
                        permuted.push(data[k][perms[i][j]]);
                    }
                    //we have the permuted array, check for the existence of 1..N
                    var timesFound = 0;
                    console.log(permuted);
                    for (var n = 1; n <= N; n++) {
                        if (permuted[n].indexOf(n) > -1) {
                            timesFound++;
                        }
                    }
                    //if timesFound=N, it worked!
                    if (timesFound === N) {
                        found.push(k);
                    }

                }

                if (found.indexOf(k) > -1) {
                    continue;
                }
            }

        }

        //found is stringy, make it have ints
        for (i = 0; i < found.length; i++) {
            found[i] = parseInt(found[i]);
        }
        return found;
    }

几年前,我写了一个回溯正则表达式引擎,当我研究你的问题时,我意识到这里可以使用相同的算法。我不确定这是否是最有效的解决方案,但这是一种选择

<强> [更新:参见JavaScript端口的答案结束] ./Stult>我在C++中编写代码,因为它是我最喜欢的语言,而且从你的问题的原修订中不清楚这是哪种语言。(我在第二次修订之前开始编写代码,在我看来,从第1版中,您的问题是语言不可知的),但我确信应该将此从C++移植到JavaScript;<代码> STD::向量< /代码>将成为<代码>数组< /代码> /<代码>对于初学者来说,

std::map
将成为一个普通的旧
对象
/
{}

C++ 为了演示如何轻松地使用此代码测试来自shell的不同输入,这里有几个简单的例子:

./a;
## ----- parsed arrays -----
## ----- KMap -----
## ----- solving algorithm -----
## ----- solution -----
## []

./a '[]';
## ----- parsed arrays -----
## A[1] == []
## ----- KMap -----
## ----- solving algorithm -----
## ----- solution -----
## []

./a '[[1,1]]';
## ----- parsed arrays -----
## A[1] == [[1,1]]
## ----- KMap -----
## 1: [1:[1]]
## ----- solving algorithm -----
## -> k=1 n=1
## [1,1] got in array 1
## burn:[X]
## candidate:[1]
## *** SUCCESS ***: k=1 has array order [1]
## ----- solution -----
## [1]

./a '[[1,1],[2,2]]' '[[2,1],[1,2],[3,2],[3,1]]';
## ----- parsed arrays -----
## A[1] == [[1,1],[2,2]]
## A[2] == [[2,1],[1,2],[3,2],[3,1]]
## ----- KMap -----
## 1: [1:[1],2:[2]]
## 2: [1:[2],2:[1]]
## 3: [1:[2],2:[2]]
## ----- solving algorithm -----
## -> k=1 n=1
## [1,1] got in array 1
## burn:[X,O]
## candidate:[1,-]
## -> k=1 n=2
## [1,2] got in array 2
## burn:[X,X]
## candidate:[1,1]
## *** SUCCESS ***: k=1 has array order [1,2]
## -> k=2 n=1
## [2,1] got in array 2
## burn:[O,X]
## candidate:[1,-]
## -> k=2 n=2
## [2,2] got in array 1
## burn:[X,X]
## candidate:[1,1]
## *** SUCCESS ***: k=2 has array order [2,1]
## -> k=3 n=1
## [3,1] got in array 2
## burn:[O,X]
## candidate:[1,-]
## -> k=3 n=2
## [3,2] failed
## burn:[O,O]
## candidate:[1,-]
## -> k=3 n=1
## [3,1] failed, can't backtrack
## *** FAILED ***: k=3
## ----- solution -----
## [1,2]
解释 这相当复杂,但我会尽力解释它是如何工作的

首先,我编写了很多“脚手架”代码,只是为了从命令行参数解析数组数据。每个命令行参数都应该包含一个数组,由
[]
分隔,在
[k,n]中有零对或多对
format在外括号内。配对本身可以用逗号分隔,但我将其设置为可选。空格几乎可以放在任何地方,当然,除了单个数字之外

我在解析期间和解析之后都进行非常严格的验证。解析后验证包括强制所有
n
值(这是每对中的第二个数字)的范围仅超过
1..n
(其中
n
当然是根据提供的数组数自动确定的),并强制执行任何单个数组中都不存在重复对,这是您在问题中指定的要求,也是我的算法所依赖的要求

因此,高级过程是(1)解析加上基本语法验证,(2)解析验证后,(3)求解,以及(4)打印解决方案。我还包括了不同时间的详细调试输出,正如您从演示中看到的

关于实际的求解算法,首先要认识到的是所有
k
s都是相互独立的;每对的第一个数字有效地隔离了它与
k
的相关性。因此,我设计了数据结构和算法,分别处理每个可能的
k
值uld代表了问题的解决方案

第二件要认识到的事情是,对于给定的
k
,由于您需要
[k,n]
存在于某个数组中,以便
1..n
中的
n
的所有值都存在于该数组中,并且您不允许将同一数组分配给不同的
n
值,并且确实存在
n
数组,因此您真正要做的是“分配”或在
n
的所有值之间“分布”数组

第三件要认识的事情是,对于每一对
[k,n]
来说,该对可以包含在多个数组中,因此可能有多个“候选者”“对于分配给给定
n
的数组。这意味着我们需要检查数组对可能
n
值的不同分配,以确定是否有任何方法将所有数组分配给
n
的所有值。”

该算法首先扫描所有数组中的所有对,并构建一个包含给定
[k,n]
数组的“候选列表”
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <climits>
#include <cerrno>
#include <cctype>

#include <vector>
#include <map>

typedef std::pair<int,int> Pair;
typedef std::vector<Pair> Array;
typedef std::vector<Array> ArrayList;

typedef std::vector<int> Solution;

Solution solve(const ArrayList& arrayList);
Array parseArray(char*& arg, int i, char* argFull );
Pair parsePair(char*& arg, int i, char* argFull );
int parseNumber(char*& arg, int i, char* argFull );
void validateArrayList(const ArrayList& arrayList);
void printArrayList(const ArrayList& arrayList);
void printSolution(const Solution& solution);
void skipWhite(char*& a);

int main(int argc, char *argv[] ) {

    // parse out arrays
    ArrayList arrayList;
    for (int i = 1; i < argc; ++i) {
        char* arg = argv[i];
        arrayList.push_back(parseArray(arg,i,arg));
    } // end for
    validateArrayList(arrayList);
    printArrayList(arrayList);

    // solve
    Solution solution = solve(arrayList);
    printSolution(solution);

} // end main()

Solution solve(const ArrayList& arrayList) {

    typedef std::vector<int> ArrayHaveList; // use the 0-based index for ease
    typedef std::vector<ArrayHaveList> SmallNList;
    typedef std::map<int,SmallNList> KMap;

    // 1: collect all possible k's into a map
    // during this collection, will precompute which arrays have which "small n" values for each k
    KMap kmap;
    for (int ai = 0; ai < arrayList.size(); ++ai) {
        const Array& array = arrayList[ai];
        for (int pi = 0; pi < array.size(); ++pi) {
            const Pair& pair = array[pi];
            std::pair<KMap::iterator,bool> insertRet = kmap.insert(std::pair<int,SmallNList>(pair.first,SmallNList()));
            SmallNList& smallNList = insertRet.first->second;
            if (insertRet.second) smallNList.resize(arrayList.size());
            assert(pair.second >= 1 && pair.second <= arrayList.size()); // should already have been validated
            smallNList[pair.second-1].push_back(ai); // don't have to check because same pair cannot appear in same array more than once (already validated this)
        } // end for
    } // end for
    // debug kmap
    std::printf("----- KMap -----\n");
    for (KMap::iterator it = kmap.begin(); it != kmap.end(); ++it) {
        int k = it->first;
        const SmallNList& smallNList = it->second;
        std::printf("%d: [", k );
        for (int ni = 0; ni < smallNList.size(); ++ni) {
            const ArrayHaveList& arrayHaveList = smallNList[ni];
            if (ni > 0) std::printf(",");
            std::printf("%d:[", ni+1 );
            for (int ci = 0; ci < arrayHaveList.size(); ++ci) {
                int ai = arrayHaveList[ci];
                if (ci > 0) std::printf(",");
                std::printf("%d", ai+1 );
            } // end for
            std::printf("]");
        } // end for
        std::printf("]\n");
    } // end for

    // 2: for each k, and then for each small n, go through each possible "candidate" array that has that small n value for that k, and see if we can satisfy all small n values for that k
    std::printf("----- solving algorithm -----\n");
    Solution solution;
    for (KMap::iterator it = kmap.begin(); it != kmap.end(); ++it) {

        int k = it->first;
        const SmallNList& smallNList = it->second;

        // actually, first do a trivial check to see if any small n values have no candidates at all; then this is impossible, and we can reject this k
        bool impossible = false; // assumption
        for (int ni = 0; ni < smallNList.size(); ++ni) { // ni is "small n index", meaning 0-based
            const ArrayHaveList& arrayHaveList = smallNList[ni];
            if (arrayHaveList.size() == 0) {
                impossible = true;
                break;
            } // end if
        } // end for
        if (impossible) {
            std::printf("trivially impossible: k=%d.\n", k );
            continue;
        } // end if

        // now run the main backtracking candidate selection algorithm
        std::vector<bool> burnList; // allows quickly checking if a candidate array is available for a particular [k,n] pair (indexed by ai)
        std::vector<int> currentCandidateIndex; // required for backtracking candidate selections; keeps track of the current selection from the list of candidates for each [k,n] pair (indexed by ni)
        burnList.assign(arrayList.size(), false );
        currentCandidateIndex.assign(arrayList.size(), -1 ); // initialize to -1, so on first iteration of any given ni it will automatically be incremented to the first index, zero
        int ni; // declared outside for-loop for accessibility afterward
        for (ni = 0; ni < smallNList.size(); ++ni) { // ni is "small n index", meaning 0-based representation of small n

            // very important: this loop is the base for the backtracking algorithm, thus, it will be continued (with modified ni) when we need to backtrack, thus, it needs to be general for these cases

            std::printf("-> k=%d n=%d\n", k, ni+1 );

            const ArrayHaveList& arrayHaveList = smallNList[ni];

            // attempt to find a candidate array that is unburnt
            bool gotOne = false; // assumption
            for (int ci = currentCandidateIndex[ni]+1; ci < arrayHaveList.size(); ++ci) { // start ci at previous best candidate index plus one
                int candidateArrayIndex = arrayHaveList[ci];
                if (!burnList[candidateArrayIndex]) { // available
                    // now we can take this array for this [k,n] pair
                    burnList[candidateArrayIndex] = true;
                    currentCandidateIndex[ni] = ci;
                    gotOne = true;
                    break;
                } // end if
            } // end for

            // react to success or failure of finding an available array for this [k,n] pair
            int niSave = ni; // just for debugging
            if (!gotOne) {
                // we were unable to find a candidate for this [k,n] pair that inhabits a currently-available array; thus, must backtrack previous small n values
                if (ni == 0) { // uh-oh; we can't backtrack at all, thus this k is not a solution; break out of ni loop
                    std::printf("[%d,%d] failed, can't backtrack\n", k, ni+1 );
                    break;
                } // end if
                // now we have ni > 0, so we can backtrack to previous ni's
                int nip = ni-1;
                const ArrayHaveList& prevArrayHaveList = smallNList[nip];
                int cip = currentCandidateIndex[nip]; // get currently-used candidate index for this previous small n
                int aip = prevArrayHaveList[cip]; // get actual currently-used array index for this previous small n
                // unconditionally unburn it
                burnList[aip] = false;
                // reset outselves (current candidate index for current iteration ni) back to -1, so when we "return" to this ni from the backtrack, it'll be ready to check again
                // note: we don't have to reset the burn list element, because it can't have been set to true for the current iteration ni; that only happens when we find an available array
                currentCandidateIndex[ni] = -1;
                // reset the iteration var ni to nip-1, so that it will be incremented back into nip
                ni = nip-1;
            } // end if

            // debug burn list, current candidate index, and decision made by the above loop (not in that order)
            // decision
            if (gotOne)
                std::printf("[%d,%d] got in array %d\n", k, niSave+1, arrayHaveList[currentCandidateIndex[niSave]]+1 );
            else
                std::printf("[%d,%d] failed\n", k, niSave+1 );
            // burn list
            std::printf("burn:[");
            for (int bi = 0; bi < burnList.size(); ++bi) {
                if (bi > 0) std::printf(",");
                std::printf("%s", burnList[bi] ? "X" : "O" );
            } // end for
            std::printf("]\n");
            // current candidate index
            std::printf("candidate:[");
            for (int ni2 = 0; ni2 < currentCandidateIndex.size(); ++ni2) {
                if (ni2 > 0) std::printf(",");
                int ci = currentCandidateIndex[ni2];
                if (ci == -1)
                    std::printf("-");
                else
                    std::printf("%d", ci+1 );
            } // end for
            std::printf("]\n");

        } // end for
        // test if we reached the end of all ni's
        if (ni == smallNList.size()) {
            std::printf("*** SUCCESS ***: k=%d has array order [", k );
            for (int ni = 0; ni < currentCandidateIndex.size(); ++ni) {
                const ArrayHaveList& arrayHaveList = smallNList[ni];
                int ci = currentCandidateIndex[ni];
                int ai = arrayHaveList[ci];
                if (ni > 0) std::printf(",");
                std::printf("%d", ai+1 );
            } // end for
            std::printf("]\n");
            solution.push_back(k);
        } else {
            std::printf("*** FAILED ***: k=%d\n", k );
        } // end if

    } // end for

    return solution;

} // end solve()

Array parseArray(char*& arg, int i, char* argFull ) {
    skipWhite(arg);
    if (*arg != '[') { std::fprintf(stderr, "invalid syntax from \"%s\": no leading '[': argument %d \"%s\".\n", arg, i, argFull ); std::exit(1); }
    ++arg;
    skipWhite(arg);
    Array array;
    while (true) {
        if (*arg == ']') {
            ++arg;
            skipWhite(arg);
            if (*arg != '\0') { std::fprintf(stderr, "invalid syntax from \"%s\": trailing characters: argument %d \"%s\".\n", arg, i, argFull ); std::exit(1); }
            break;
        } else if (*arg == '[') {
            array.push_back(parsePair(arg,i,argFull));
            skipWhite(arg);
            // allow single optional comma after any pair
            if (*arg == ',') {
                ++arg;
                skipWhite(arg);
            } // end if
        } else if (*arg == '\0') {
            std::fprintf(stderr, "invalid syntax from \"%s\": unexpected end-of-array: argument %d \"%s\".\n", arg, i, argFull ); std::exit(1);
        } else {
            std::fprintf(stderr, "invalid syntax from \"%s\": invalid character '%c': argument %d \"%s\".\n", arg, *arg, i, argFull ); std::exit(1);
        } // end if
    } // end for
    return array;
} // end parseArray()

Pair parsePair(char*& arg, int i, char* argFull ) {
    assert(*arg == '[');
    ++arg;
    skipWhite(arg);
    int first = parseNumber(arg,i,argFull);
    skipWhite(arg);
    if (*arg != ',') { std::fprintf(stderr, "invalid syntax from \"%s\": non-',' after number: argument %d \"%s\".\n", arg, i, argFull ); std::exit (1); }
    ++arg;
    skipWhite(arg);
    int second = parseNumber(arg,i,argFull);
    skipWhite(arg);
    if (*arg != ']') { std::fprintf(stderr, "invalid syntax from \"%s\": non-']' after number: argument %d \"%s\".\n", arg, i, argFull ); std::exit (1); }
    ++arg;
    return Pair(first,second);
} // end parsePair()

int parseNumber(char*& arg, int i, char* argFull ) {
    char* endptr;
    unsigned long landing = strtoul(arg, &endptr, 10 );
    if (landing == 0 && errno == EINVAL || landing == ULONG_MAX && errno == ERANGE || landing > INT_MAX) {
        std::fprintf(stderr, "invalid syntax from \"%s\": invalid number: argument %d \"%s\".\n", arg, i, argFull );
        std::exit(1);
    } // end if
    arg = endptr;
    return (int)landing;
} // end parseNumber()

void validateArrayList(const ArrayList& arrayList) {
    // note: all numbers have already been forced to be natural numbers during parsing
    // 1: validate that all seconds are within 1..N
    // 2: validate that we don't have duplicate pairs in the same array
    typedef std::vector<bool> SecondSeenList;
    typedef std::map<int,SecondSeenList> FirstMap;
    for (int ai = 0; ai < arrayList.size(); ++ai) {
        const Array& array = arrayList[ai];
        FirstMap firstMap;
        for (int pi = 0; pi < array.size(); ++pi) {
            const Pair& pair = array[pi];
            if (pair.second == 0) { std::fprintf(stderr, "invalid second number %d: less than 1.\n", pair.second ); std::exit(1); }
            if (pair.second > arrayList.size()) { std::fprintf(stderr, "invalid second number %d: greater than N=%d.\n", pair.second, arrayList.size() ); std::exit(1); }
            std::pair<FirstMap::iterator,bool> insertRet = firstMap.insert(std::pair<int,SecondSeenList>(pair.first,SecondSeenList()));
            SecondSeenList& secondSeenList = insertRet.first->second;
            if (insertRet.second) secondSeenList.assign(arrayList.size(), false );
            if (secondSeenList[pair.second-1]) { std::fprintf(stderr, "invalid array %d: duplicate pair [%d,%d].\n", ai+1, pair.first, pair.second ); std::exit(1); }
            secondSeenList[pair.second-1] = true;
        } // end for
    } // end for
} // end validateArrayList()

void printArrayList(const ArrayList& arrayList) {
    std::printf("----- parsed arrays -----\n");
    for (int ai = 0; ai < arrayList.size(); ++ai) {
        const Array& array = arrayList[ai];
        std::printf("A[%d] == [", ai+1 );
        for (int pi = 0; pi < array.size(); ++pi) {
            const Pair& pair = array[pi];
            if (pi > 0) std::printf(",");
            std::printf("[%d,%d]", pair.first, pair.second );
        } // end for
        std::printf("]\n");
    } // end for
} // end printArrayList()

void printSolution(const Solution& solution) {
    std::printf("----- solution -----\n");
    std::printf("[");
    for (int si = 0; si < solution.size(); ++si) {
        int k = solution[si];
        if (si > 0) std::printf(",");
        std::printf("%d", k );
    } // end for
    std::printf("]\n");
} // end printSolution()

void skipWhite(char*& a) {
    while (*a != '\0' && std::isspace(*a))
        ++a;
} // end skipWhite()
ls;
## a.cpp

g++ a.cpp -o a;

ls;
## a.cpp  a.exe*

./a '[ [3,2], [4,1], [5,1], [7,1], [7,2], [7,3] ]' '[ [3,1], [3,2], [4,1], [4,2], [4,3], [5,3], [7,2] ]' '[ [4,1], [5,1], [5,2], [7,1]]';
## ----- parsed arrays -----
## A[1] == [[3,2],[4,1],[5,1],[7,1],[7,2],[7,3]]
## A[2] == [[3,1],[3,2],[4,1],[4,2],[4,3],[5,3],[7,2]]
## A[3] == [[4,1],[5,1],[5,2],[7,1]]
## ----- KMap -----
## 3: [1:[2],2:[1,2],3:[]]
## 4: [1:[1,2,3],2:[2],3:[2]]
## 5: [1:[1,3],2:[3],3:[2]]
## 7: [1:[1,3],2:[1,2],3:[1]]
## ----- solving algorithm -----
## trivially impossible: k=3.
## -> k=4 n=1
## [4,1] got in array 1
## burn:[X,O,O]
## candidate:[1,-,-]
## -> k=4 n=2
## [4,2] got in array 2
## burn:[X,X,O]
## candidate:[1,1,-]
## -> k=4 n=3
## [4,3] failed
## burn:[X,O,O]
## candidate:[1,1,-]
## -> k=4 n=2
## [4,2] failed
## burn:[O,O,O]
## candidate:[1,-,-]
## -> k=4 n=1
## [4,1] got in array 2
## burn:[O,X,O]
## candidate:[2,-,-]
## -> k=4 n=2
## [4,2] failed
## burn:[O,O,O]
## candidate:[2,-,-]
## -> k=4 n=1
## [4,1] got in array 3
## burn:[O,O,X]
## candidate:[3,-,-]
## -> k=4 n=2
## [4,2] got in array 2
## burn:[O,X,X]
## candidate:[3,1,-]
## -> k=4 n=3
## [4,3] failed
## burn:[O,O,X]
## candidate:[3,1,-]
## -> k=4 n=2
## [4,2] failed
## burn:[O,O,O]
## candidate:[3,-,-]
## -> k=4 n=1
## [4,1] failed, can't backtrack
## *** FAILED ***: k=4
## -> k=5 n=1
## [5,1] got in array 1
## burn:[X,O,O]
## candidate:[1,-,-]
## -> k=5 n=2
## [5,2] got in array 3
## burn:[X,O,X]
## candidate:[1,1,-]
## -> k=5 n=3
## [5,3] got in array 2
## burn:[X,X,X]
## candidate:[1,1,1]
## *** SUCCESS ***: k=5 has array order [1,3,2]
## -> k=7 n=1
## [7,1] got in array 1
## burn:[X,O,O]
## candidate:[1,-,-]
## -> k=7 n=2
## [7,2] got in array 2
## burn:[X,X,O]
## candidate:[1,2,-]
## -> k=7 n=3
## [7,3] failed
## burn:[X,O,O]
## candidate:[1,2,-]
## -> k=7 n=2
## [7,2] failed
## burn:[O,O,O]
## candidate:[1,-,-]
## -> k=7 n=1
## [7,1] got in array 3
## burn:[O,O,X]
## candidate:[2,-,-]
## -> k=7 n=2
## [7,2] got in array 1
## burn:[X,O,X]
## candidate:[2,1,-]
## -> k=7 n=3
## [7,3] failed
## burn:[O,O,X]
## candidate:[2,1,-]
## -> k=7 n=2
## [7,2] got in array 2
## burn:[O,X,X]
## candidate:[2,2,-]
## -> k=7 n=3
## [7,3] got in array 1
## burn:[X,X,X]
## candidate:[2,2,1]
## *** SUCCESS ***: k=7 has array order [3,2,1]
## ----- solution -----
## [5,7]
./a;
## ----- parsed arrays -----
## ----- KMap -----
## ----- solving algorithm -----
## ----- solution -----
## []

./a '[]';
## ----- parsed arrays -----
## A[1] == []
## ----- KMap -----
## ----- solving algorithm -----
## ----- solution -----
## []

./a '[[1,1]]';
## ----- parsed arrays -----
## A[1] == [[1,1]]
## ----- KMap -----
## 1: [1:[1]]
## ----- solving algorithm -----
## -> k=1 n=1
## [1,1] got in array 1
## burn:[X]
## candidate:[1]
## *** SUCCESS ***: k=1 has array order [1]
## ----- solution -----
## [1]

./a '[[1,1],[2,2]]' '[[2,1],[1,2],[3,2],[3,1]]';
## ----- parsed arrays -----
## A[1] == [[1,1],[2,2]]
## A[2] == [[2,1],[1,2],[3,2],[3,1]]
## ----- KMap -----
## 1: [1:[1],2:[2]]
## 2: [1:[2],2:[1]]
## 3: [1:[2],2:[2]]
## ----- solving algorithm -----
## -> k=1 n=1
## [1,1] got in array 1
## burn:[X,O]
## candidate:[1,-]
## -> k=1 n=2
## [1,2] got in array 2
## burn:[X,X]
## candidate:[1,1]
## *** SUCCESS ***: k=1 has array order [1,2]
## -> k=2 n=1
## [2,1] got in array 2
## burn:[O,X]
## candidate:[1,-]
## -> k=2 n=2
## [2,2] got in array 1
## burn:[X,X]
## candidate:[1,1]
## *** SUCCESS ***: k=2 has array order [2,1]
## -> k=3 n=1
## [3,1] got in array 2
## burn:[O,X]
## candidate:[1,-]
## -> k=3 n=2
## [3,2] failed
## burn:[O,O]
## candidate:[1,-]
## -> k=3 n=1
## [3,1] failed, can't backtrack
## *** FAILED ***: k=3
## ----- solution -----
## [1,2]