[Leetcode] "All-in-one" problem series (i)-generation of full permutations with commutative element method and their applications, examples: permutations I and II, N-queens I and II, Sudoku

Source: Internet
Author: User

Ext.: http://www.cnblogs.com/felixfang/p/3705754.html

First, the outset

permutation, arranging problems. This blog post with a few leetcode of the topic and quoted the sword refers to the offer on an example to start, small talk about the solution of this type of problem.

Second, get started

The most typical permutation topic is this:

Given A collection of numbers, return all possible permutations.

For example,
[1,2,3]The following permutations:
[1,2,3],,,,, [1,3,2] [2,1,3] [2,3,1] [3,1,2] and [3,2,1] .

Class Solution {public:    vector<vector<int> > Permute (vector<int> &num) {    }};

My first contact with this kind of problem is in the sword-point offer, see note 28 (*), the Arrangement of strings (the typical solution for permutation problems: recursive, each exchange of the first element and the position of one of the remaining elements).

The method used in the book is "Exchange element", the advantage of this method is that there is no need to open a new array to save the temporary solution, thereby saving some of the auxiliary space.

The idea of the interchange method is for (i = start to end), in the Loop: Swap (first and first I), recursive call (start+1), swap back

According to this idea, you can easily write the code of this problem:

Class Solution {public:    vector<vector<int> > Permute (vector<int> &num) {        if (num.size () = = 0) return res;        Permutecore (num, 0);        return res;    } Private:    vector<vector<int> > Res;    void Permutecore (vector<int> &num, int start) {        if (start = = Num.size ()) {            vector<int> v;            for (Vector<int>::iterator i = Num.begin (); I < Num.end (); ++i) {                v.push_back (*i);            }            Res.push_back (v);        }        for (int i = start; I < num.size (); ++i) {            swap (num, start, i);            Permutecore (num, start+1);            Swap (num, start, i);        }    }    void swap (vector<int> &num, int left, int. right) {        int tmp = Num[left];        Num[left] = Num[right];        Num[right] = tmp;    }};

permutation II is based on the previous question, adding the "array element may repeat" condition.

Thus, if the interchange method is used, a set is defined to store the value of the element that has already been exchanged.

Class Solution {public:    vector<vector<int> > Permuteunique (vector<int> &num) {        if ( Num.size () <= 0) return res;        Permcore (num, 0);        return res;    } Private:    vector<vector<int> > Res;    void Permcore (vector<int> &num, int st) {        if (st = = Num.size ()) res.push_back (num);        else{            set<int> SWP;            for (int i = st; i < num.size (); ++i) {                if (Swp.find (num[i])! = Swp.end ()) continue;                Swp.insert (Num[i]);                Swap (num, St, i);                Permcore (num, st+1);                Swap (num, St, i);        }}} void swap (vector<int> &num, int left, int. right) {        int tmp = Num[left];        Num[left] = Num[right];        Num[right] = tmp;    }};

Digression: The Exchange method is only one of the solutions, in fact, we can learn from next permuation's ideas (see the second chapter of this series) to solve this problem, thus eliminating the use of recursion.

Use next permutation's idea to solve permutation II

Class Solution {public:    vector<vector<int> > Permuteunique (vector<int> &num) {        if ( Num.size () <= 0) return res;        Sort (Num.begin (), Num.end ());        Res.push_back (num);        int i = 0, j = 0;        while (1) {            //calculate next permutation for            (i = num.size ()-2; I >= 0 && num[i] >= num[i+1];-i); 
   
    if (i < 0) break;            for (j = num.size ()-1; J > I && num[j] <= num[i];--j);            Swap (num, I, j);            j = num.size ()-1;            ++i;            while (I < j)                swap (num, i++, j--);            Push next permutation            res.push_back (num);        }        return res;    } Private:    vector<vector<int> > Res;    void swap (vector<int> &num, int left, int. right) {        int tmp = Num[left];        Num[left] = Num[right];        Num[right] = tmp;    }};
   

Third, the application

A typical application of permutation class problem is the N queen problem, taking the n-queens question and n-queens II as an example in Leetcode:

N-queens

The n-queens Puzzle is the problem of placing N Queens on a nxn chessboard such that No, Queens attack.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the nth-queens ' placement, where ‘Q‘ and ‘.‘ both in Dicate a queen and an empty space respectively.

For example,
There exist-distinct solutions to the 4-queens puzzle:

[ [". Q.. ",  //solution 1  " ... Q ",  " Q ... ",".  . Q. "], ["]. Q. ",  //Solution 2  " Q ... ",  " ... Q ",  ". Q.. "]
Class Solution {public:    vector<vector<string> > solvenqueens (int n) {            }};

Above is the content of the title N-queens , the title N-queens II is actually more easy, it requires the same, just do not need to return all the solution, as long as the number of solutions returned.

With the above idea, if using a[i] = J to indicate that the queen of line I is placed on column J, N-queen is also a full-permutation problem, but the arrangement requires an additional judgment, that is, whether the two queens are on a diagonal line.

I made a mistake when I really realized it.

As mentioned above, the idea of the interchange method is for (i = start to end), in the loop: switch (start and i), recursive call (start+1), switch back

I mistakenly think that N queen does not need switch back, in fact, switch back is a must to do the steps, because the essence of this solution is still deep search, sub-recursive will layer calls down, not in time Swtich back words, The next recursive call to the current layer will switch the duplicate value over, resulting in duplication, with the result that some of the correct permutations are missing. Therefore, when you use the interchange method to solve the whole permutation problem, you cannot disturb the arrangement of recursive calls.

Question N-queens by the AC code:

Class Solution {public:vector<vector<string> > Solvenqueens (int n) {if (n <= 0) return res;        int* A = new Int[n];        for (int i = 0; i < n; ++i) a[i] = i;        Nqueenscore (A, 0, N);    return res;    }private:vector<vector<string> > Res; void Nqueenscore (int a[], int start, int n) {if ((start+1) = = N && judgeattackdiag (A, start)) out        Put (A, n);                else{for (int i = start; i < n; ++i) {Swtich (A, start, i);                if (Judgeattackdiag (A, start)) Nqueenscore (A, start+1, N);            Swtich (A, start, i);        }}} void Swtich (int a[], int left, int. right) {int temp = A[left];        A[left] = A[right];    A[right] = temp; } bool Judgeattackdiag (int a[], int newplace) {   //everytime A New Place was configured out, judge if it Can be attacked by the existing Queens if (Newplace <= 0) return true;        BOOL Canattack = false; for (int i = 0; i < Newplace; ++i) {if (newplace-i) = = (A[newplace]-a[i]) | |        (i-newplace) = = (A[newplace]-a[i])) Canattack = true;    } return!canattack;        } void output (int a[], int n) {vector<string> V;            for (int i = 0; i < n; ++i) {string row (n, '. ');        V.push_back (row);          } for (int j = 0; J < N; ++j) {v[a[j]][j] = ' Q ';    } res.push_back (v); }};

N-queens II

Follow up for n-queens problem.

Now, instead outputting board configurations and return the total number of distinct solutions.

Class Solution {public:    int totalnqueens (int n) {    }};

The basic idea is still to use the whole arrangement, this time the code can be written concise.

Class Solution {public:    int totalnqueens (int n) {        if (n <= 1) return n;        res = 0;        Queens = new Int[n];        for (int i = 0; i < n; queens[i] = i, ++i);        Nqueenscore (Queens, n, 0);        return res;    } Private:    int res;    int* Queens;    void Nqueenscore (int* queens, int n, int st) {        if (st = = n) ++res;        int tmp, I, J;        for (i = st; i < n; ++i) {            tmp = queens[st];            QUEENS[ST] = queens[i];            Queens[i] = tmp;                        for (j = 0, J < St; ++j) {                if (ABS (Queens[st]-queens[j]) = = ABS (ST-J)) break            ;            if (j = = st) Nqueenscore (Queens, N, st+1);                        TMP = queens[st];            QUEENS[ST] = queens[i];            Queens[i] = tmp;}}    ;

I still made the mistake of forgetting switch back the first time I submitted it, and the first time I committed it was "if (ABS (Queens[st]-queens[j) = = = ABS (ST-J)) return;"

This causes the switch back part of the code (highlighted) to not be executed, which disrupts the entire order.

3. Sudoku Problem

Sudoku and N Queens, all need to constantly calculate the current position of the number placed on whether to meet the conditions, not satisfied with the backtracking, placing another number, based on this new number and then calculate.

The process of selecting a new number is the whole arrangement.

Take the example on Leetcode:

Write a program to solve a Sudoku puzzle by filling the empty cells.

Empty cells is indicated by the character ‘.‘ .

Assume that there would be is only one unique solution.

A Sudoku Puzzle ...

... and its solution numbers marked in red.

void Solvesudoku (Vector<vector<char> > &board) {}

For the rule of Sudoku, see here: Sudoku puzzles-the rules. Each row, column, and 9 3x3 squares must be guaranteed to appear only once per 1-9.

We can still use the Exchange method to solve the idea is still:

for (i = start to end), in the Loop: Swap (start and I); recursive call (start+1) if the current arrangement is correct; swap back

The extra consideration here is that there are some intrinsic numbers in the Sudoku array that cannot be moved in the first place. Therefore, I use flag[][] to mark whether a number in a position can be replaced. FLAG[I][J] = = True indicates that the number on board[i][j] is replaceable and false means not replaceable. So the idea was slightly changed, and became:

Func (start) {

A. If the flag on the start corresponding position = = False, indicating that the current bit can not be changed, so only to determine whether the current arrangement is correct, recursive call (start+1)

B. Position of start on flag = False

C. for (i = start to the end of the current line), in the Loop: Swap (start and I); recursive call (start+1) if the current arrangement is correct; swap back

D. Position of start on flag = True

}

Code:

Class Solution {Public:void Solvesudoku (vector<vector<char> > &board) {flag = new bool*[10];        FLAG[I][J] = = False means value on Board[i][j] is decided or originally given.  digits = new BOOL[10];        Digits is used to check whether one digit (1-9) are duplicated in sub 3*3 square int i = 0, j = 0;            for (; i < 9; ++i) {Flag[i] = new BOOL[9];                for (j = 0; J < 9; ++j) {if (board[i][j] = = '. ') flag[i][j] = true;            else flag[i][j] = false; }} initialboard (board, 9);        Initialize the board, fill all the vacancies first, and ensure that each row has no duplicate numbers.    Solvesudokucore (board, 0);    }private:bool **flag;    BOOL *digits;        void Initialboard (Vector<vector<char> > &board, int N) {int I, j, K;        BOOL *op = new bool[n+1];            for (i = 0; i < n; ++i) {for (j = 0; J <= N; ++j) op[j] = false; for (j = 0; j < N; ++j) {if (board[i][j]! = '.‘)            OP[BOARD[I][J]-' 0 '] = true; } for (j = 0, k = 1; j < N; ++j) {if (board[i][j] = = '. ')                    {while (op[k++]);                BOARD[I][J] = ((k-1) + ' 0 ');    }}} delete op;        } bool Check (vector<vector<char> > &board, int index) {int col = index%9, row = INDEX/9;        int i = 0;                for (i = 0; i < 9; ++i) {if (I! = row &&!flag[i][col] && board[i][col] = = Board[row][col])        return false;            } if ((col+1)%3 = = 0 && (row+1)%3 = = 0) {for (i = 0; i < ten; ++i) digits[i] = false;                     for (int j = (ROW/3), J < (row/3+1), ++j) {for (int k = (COL/3), K < (col/3+1), ++k) {                    if (Digits[board[j][k]-' 0 ']) return false;                Digits[board[j][k]-' 0 '] = true;    }}} return true;   }     BOOL Solvesudokucore (vector<vector<char> > &board, int index) {if (index = = Bayi) return true; if (!flag[index/9][index%9]) {//If the current location is immutable, then just check if it is correct if (check (board, index) && Solvesu        Dokucore (Board, index+1)) return true;            }else{//If the current position is a change, you need to replace the current bit with a swap to see which number is correct on the current bit.            FLAG[INDEX/9][INDEX%9] = false;  for (int i = index; i < (index/9+1) *9; ++i) {if (flag[i/9][i%9] | | i = = index) {int tmp                    = board[i/9][i%9];                    BOARD[I/9][I%9] = board[index/9][index%9];                    BOARD[INDEX/9][INDEX%9] = tmp; if (check (board, index) && solvesudokucore (board, index+1)) return true;                                        If the number on the current bit is correct, continue to calculate which number to put on the next one.                    TMP = board[i/9][i%9];                    BOARD[I/9][I%9] = board[index/9][index%9];    BOARD[INDEX/9][INDEX%9] = tmp;            }} flag[index/9][index%9] = true;    } return false; }};

The extension is given a sequence containing repeating elements, resulting in its full array

What if you want to build a sequence that contains duplicate elements in a full order? Take the question permutations II of the Leetcode as an example:

Given A collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2]The following unique permutations:
[1,1,2], [1,2,1] , and [2,1,1] .

Class Solution {public:    vector<vector<int> > Permuteunique (vector<int> &num) {    }};

Ideas:

For example [1, 1, 2, 2], we swapped "1" on position 1 and "3" on position 2, no longer need to swap "1" on position 1 and position 4.

Therefore, on the basis of the traditional exchange method, we need to add a filter: For example, we need to change the position 2-4 element and position 1 on the "1" exchange, at this time, if the element on the 2-4 has a duplicate value, we only need to use the first occurrence of the value and position 1 to do the exchange.

My idea was to sort the elements of position 2-4 first, then define the values of the elements that were last swapped, and then swap the values on the current value and position 1 if the current value and the pre are different.

The code implemented in this way is:

Class Solution {public:vector<vector<int> > Permuteunique (vector<int> &num) {if (num.size        () = = 0) return res;        Permutecore (num, 0);    return res;    }private:vector<vector<int> > Res;            void Permutecore (vector<int> &num, int start) {if (start = = Num.size ()) {vector<int> V;            for (Vector<int>::iterator i = Num.begin (); I < Num.end (); ++i) {v.push_back (*i);        } res.push_back (v);        } sort (Num.begin () +start, Num.end ());        int pre; for (int i = start; I < num.size (); ++i) {if (i = = Start | | Pre! = num[i]) {swap (num, start, i)                ;                Permutecore (num, start+1);                Swap (num, start, i);            Pre = Num[i];        }}} void Swap (vector<int> &num, int left, int. right) {int tmp = Num[left];        Num[left] = Num[right]; Num[right] = tmp }};

However, the result is output Limit exceeded, which analyzes the reason that the sort destroys the current sub-arrangement, resulting in repeated solutions. As I said in the previous section, when you use the interchange method to solve an entire permutation problem, you cannot disrupt the arrangement of recursive calls, or it may result in duplicate solutions.

Instead of sort, use set to go heavy. Replace the highlighted part of the above code with the highlighted part of the code below, this time AC.

Class Solution {public:vector<vector<int> > Permuteunique (vector<int> &num) {if (num.size        () = = 0) return res;        Permutecore (num, 0);    return res;        }private:vector<vector<int> > Res;            void Permutecore (vector<int> &num, int start) {if (start = = Num.size ()) {vector<int> V;            for (Vector<int>::iterator i = Num.begin (); I < Num.end (); ++i) {v.push_back (*i);        } res.push_back (v);        } set<int> used; for (int i = start; I < num.size (); ++i) {if (Used.find (num[i]) = = Used.end ()) {swap (num, start                , i);                Permutecore (num, start+1);                Swap (num, start, i);            Used.insert (Num[i]);        }}} void Swap (vector<int> &num, int left, int. right) {int tmp = Num[left];        Num[left] = Num[right];    Num[right] = tmp; }};

However, the disadvantage of this method is that the set needs to be defined in the local variable area in order to ensure that the recursive function is not mixed with set.

Five, Summary:

For the whole permutation problem, the Exchange method is a relatively basic method, the advantage is that there is no need for additional space .

Need attention when using

A. Do not disturb the sequence order of the sub-problems.

B. Remember to change back, backtracking can be carried out correctly, that is, the code responsible for the switch to be executed.

[Leetcode] "All-in-one" problem series (i)-generation of full permutations with commutative element method and their applications, examples: permutations I and II, N-queens I and II, Sudoku

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.