Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 526. Beautiful Arrangement #526

Open
grandyang opened this issue May 30, 2019 · 1 comment
Open

[LeetCode] 526. Beautiful Arrangement #526

grandyang opened this issue May 30, 2019 · 1 comment

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

Suppose you have N integers from 1 to N. We define a beautiful arrangement as an array that is constructed by these N numbers successfully if one of the following is true for the ith position (1 ≤ i ≤ N) in this array:

  1. The number at the ith position is divisible by i.
  2. i is divisible by the number at the ith position.

 

Now given N, how many beautiful arrangements can you construct?

Example 1:

Input: 2
Output: 2
Explanation: 
  
The first beautiful arrangement is [1, 2]:
  
Number at the 1st position (i=1) is 1, and 1 is divisible by i (i=1).
  
Number at the 2nd position (i=2) is 2, and 2 is divisible by i (i=2).
  
The second beautiful arrangement is [2, 1]:
  
Number at the 1st position (i=1) is 2, and 2 is divisible by i (i=1).
  
Number at the 2nd position (i=2) is 1, and i (i=2) is divisible by 1.

 

Note:

  1. N is a positive integer and will not exceed 15.

 

这道题给了我们1到N,总共N个正数,然后定义了一种优美排列方式,对于该排列中的所有数,如果数字可以整除下标,或者下标可以整除数字,那么我们就是优美排列,让我们求出所有优美排列的个数。那么对于求种类个数,或者是求所有情况,这种问题通常要用递归来做,递归简直是暴力的不能再暴力的方法了。而递归方法等难点在于写递归函数,如何确定终止条件,还有for循环中变量的起始位置如何确定。那么这里我们需要一个visited数组来记录数字是否已经访问过,因为优美排列中不能有重复数字。我们用变量pos来标记已经生成的数字的个数,如果大于N了,说明已经找到了一组排列,结果res自增1。在for循环中,i应该从1开始,因为我们遍历1到N中的所有数字,如果该数字未被使用过,且满足和坐标之间的整除关系,那么我们标记该数字已被访问过,再调用下一个位置的递归函数,之后不要忘记了恢复初始状态,参见代码如下:

 

解法一:

class Solution {
public:
    int countArrangement(int N) {
        int res = 0;
        vector<int> visited(N + 1, 0);
        helper(N, visited, 1, res);
        return res;
    }
    void helper(int N, vector<int>& visited, int pos, int& res) {
        if (pos > N) {
            ++res; 
            return;
        }
        for (int i = 1; i <= N; ++i) {
            if (visited[i] == 0 && (i % pos == 0 || pos % i == 0)) {
                visited[i] = 1;
                helper(N, visited, pos + 1, res);
                visited[i] = 0;
            }
        }
    }
};

 

上面的解法在N=4时产生的优美序列如下:

1 2 3 4
1 4 3 2
2 1 3 4
2 4 3 1
3 2 1 4
3 4 1 2
4 1 3 2
4 2 3 1

 

通过看上面的分析,是不是觉得这道题的本质其实是求全排列,然后在所有全排列中筛选出符合题意的排列。那么求全排列的另一种经典解法就是交换数组中任意两个数字的位置,来形成新的排列,参见代码如下:

 

解法二:

class Solution {
public:
    int countArrangement(int N) {
        vector<int> nums(N);
        for (int i = 0; i < N; ++i) nums[i] = i + 1;
        return helper(N, nums);
    }
    int helper(int n, vector<int>& nums) {
        if (n <= 0) return 1;
        int res = 0;
        for (int i = 0; i < n; ++i) {
            if (n % nums[i] == 0 || nums[i] % n == 0) {
                swap(nums[i], nums[n - 1]);
                res += helper(n - 1, nums);
                swap(nums[i], nums[n - 1]);
            }
        }
        return res;
    }
};

 

上面的解法在N=4时产生的优美序列如下:

2 4 3 1
4 2 3 1
3 4 1 2
4 1 3 2
1 4 3 2
3 2 1 4
2 1 3 4
1 2 3 4

 

参考资料:

https://discuss.leetcode.com/topic/79916/java-solution-backtracking

https://discuss.leetcode.com/topic/79921/my-c-elegant-solution-with-back-tracking

 

LeetCode All in One 题目讲解汇总(持续更新中...) 

@lld2006
Copy link

lld2006 commented Jul 14, 2021

感觉还是利用bit位写起来简单, 也不需要递归。m 是当插入1--k-1 这些数后所有的可能count,mnew是将k放入合适的位置后, 所有的可能的排列数。

class Solution {
public:
    int countArrangement(int N) {
      int dpsize =1< m(dpsize); 
      m[0] = 1;//before any number inserted
      for(int i = 0; i< N; ++i){//i+1 is num
        vector< int > mnew( dpsize ); //dp array for next number
        for(int j = 0; j < dpsize; ++j){
          if(m[j] == 0) continue;
          for(int pos =0; pos< N;++pos){
            if(j&(1 << pos)) continue;  
            if((i+1)%(pos+1)==0 || (pos+1)%(i+1)==0){
              mnew[j|(1 << pos)] += m[j]; 
            }
          }
        }
        m.swap(mnew);
      }
      return m[(1 << N)-1];
    }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants