力扣 526. 优美的排列(中等)

题目

假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:

  • 第 i 位的数字能被 i 整除
  • i 能被第 i 位上的数字整除
  • 现在给定一个整数 N,请问可以构造多少个优美的排列?

示例1:

输入: 2
输出: 2
解释:1 个优美的排列是 [1, 2]:1 个位置(i=1)上的数字是11能被 i(i=1)整除
  第 2 个位置(i=2)上的数字是22能被 i(i=2)整除

第 2 个优美的排列是 [2, 1]:1 个位置(i=1)上的数字是22能被 i(i=1)整除
  第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除

说明:

  • N 是一个正整数,并且不会超过15。

题解

方法一:回溯法
因为N不会超过15,所以可以使用回溯法,不用担心超时问题。
构建备选数集合后, 依次不放回的选中某个数, 要求取出数满足一定条件, 求最终将集合中元素全部取出的方法的个数。
回溯模板:
a. 定义集合List left为当前状态下,存储可选数的备选元素集合(初始化为1~N的元素集合)。
b. 在 left 集合中选取第 i 个位置的元素时,遍历 left集合, 当遍历到的元素 x 满足条件: x % i = = 0 ∣ ∣ i % x = = 0 x\%i==0 || i\%x==0 x%i==0i%x==0 时, 说明在当前位置, 取当前元素 x 可以满足要求,那么可以从备选元素集合中删除 x , 进入第 i+1 位的遍历, 此即递归的循环体;
c. 递归的退出条件即为: 剩余元素的集合left的集合大小等于0,此时的排列是题目要求求得的排列的一种。

class Solution {

    int ans = 0;
    
    public int countArrangement(int n) {
        // 回溯法
        List<Integer> left = new ArrayList<>();   //构建存储可选数的备选列表
        for(int i = 1; i <= n; i++){  // 初始化
            left.add(i);
        }
        dfs(left, 1);  // left是备选列表,1是当前位置
        return ans;
    }

    private void dfs(List<Integer> left, int index){
        if(left.size() == 0){  // 说明所有数都放到了对应位置
            ans++;
            return;
        }
        for(int x : left){
            if(x % index == 0 || index % x == 0){   // 该位置可以放x
                List<Integer> copy = new ArrayList<>(left);   // 这里不能先删再加,会打乱顺序,采用复制列表的方式
                // List删除元素时传入数字时,默认按索引删除。如果需要删除Integer对象,调用remove(object)方法,需要传入Integer类型。
                copy.remove(new Integer(x));   // 将当前元素从备选列表中删除                
                dfs(copy, index + 1);
            }
        }
    }
}

力扣官方方法
采用了visited数组来标记是否使用,以及match数组来预处理获取每个位置满足要求的数。

class Solution {
    List<Integer>[] match;
    boolean[] vis;
    int num;

    public int countArrangement(int n) {
        vis = new boolean[n + 1];
        match = new List[n + 1];
        for (int i = 0; i <= n; i++) {
            match[i] = new ArrayList<Integer>();
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (i % j == 0 || j % i == 0) {
                    match[i].add(j);
                }
            }
        }
        backtrack(1, n);
        return num;
    }

    public void backtrack(int index, int n) {
        if (index == n + 1) {
            num++;
            return;
        }
        for (int x : match[index]) {
            if (!vis[x]) {
                vis[x] = true;
                backtrack(index + 1, n);
                vis[x] = false;
            }
        }
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/beautiful-arrangement/solution/you-mei-de-pai-lie-by-leetcode-solution-vea2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值