Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.
算法一
利用permutation中使用swap的思路,可以快速满足列条件的限制。
这样,在检查合法性时,只需要检查是否满足对角线的限制即可。
此算法在leetcode上的实际的执行时间为4ms。
class Solution { public: int totalNQueens(int n) { int sum = 0; vector<int> solution; for (int i=0; i<n; i++) solution.push_back(i); helper(solution, 0, sum); return sum; } void helper(vector<int> &solution, int start, int &sum) { if (start == solution.size()) { ++sum; return; } for (int i=start; i<solution.size(); i++) { swap(solution[i], solution[start]); if (isValid(solution, start)) { helper(solution, start+1, sum); } swap(solution[i], solution[start]); } } bool isValid(vector<int> &solution, int start) { for (int i=start-1; i>=0; --i) { if (start-i == abs(solution[start]-solution[i])) return false; } return true; } };
算法二:
基于回溯法。
特点,利用3个数组,分别记录剩下的列,以及由左对角线,右对角线的限制。
这样在回溯时,可以快速确定某个位置,是否可用。
此算法在leetcode上实际执行时间为3ms。
注,这里选择vector<char>,而不是vector<bool>。这是基于性能考虑,如果选择vector<bool>,执行时间会达到8ms。
原因,则是因为stl 对vector<bool>作了比特化的特化,虽说是省了空间,但却是以时间为代价。
class Solution { public: int totalNQueens(int n) { int sum = 0; vector<int> cl; // column vector<char> ld(n*2); //left diagonal vector<char> rd(n*2); // right diagonal for (int i=0; i<n; i++) cl.push_back(i); helper(cl, 0, ld, 0, rd, n, sum); return sum; } void helper(vector<int> &cl, int start_cl, vector<char> &ld, int start_ld, vector<char> &rd, int start_rd, int &sum) { if (start_cl == cl.size()) { ++sum; return; } for (int i=start_cl; i<cl.size(); i++) { const int ldi = start_ld + cl[i]; const int rdi = start_rd + cl[i]; if (!ld[ldi] && !rd[rdi]) { ld[ldi] = rd[rdi] = true; swap(cl[i], cl[start_cl]); helper(cl, start_cl+1, ld, start_ld+1, rd, start_rd-1, sum); swap(cl[i], cl[start_cl]); ld[ldi] = rd[rdi] = false; } } } };
算法二,与算法一的主要差别是:
算法二用
!ld[ldi] && !rd[rdi]
这一句代替了算法一中isValid() 函数。
时间: 2024-11-01 16:09:25