算法练习(1)—— 简单递归/回溯

本文是作者进行算法学习的实践记录,详细探讨了LeetCode中的第526题——Beautiful Arrangement。作者解释了题目的要求,即数组中的每个数要么能被其位置i整除,要么i能被该数整除,并给出了N=4的解决方案。通过枚举和剪枝的方法,实现了问题的解答,代码使用了set数据结构。文章邀请读者分享更优的解题思路。

算法练习(1)—— 简单递归/回溯

前言

算法课的一环。题目一般都是从leetcode里拿,希望自己在学习中能有所提升吧,也希望自己能坚持下去。

习题

这次的例题是在 Backtracking 栏目里找的第一题。
标题为 526. Beautiful Arrangement


懒得点链接的同学就直接看我下面的搬运吧~

Description

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?

Note

N is a positive integer and will not exceed 15.

Example

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.


思路与代码

1.首先理解一下题意,大概就是每个数放在对应的第i位,要么它能整除i,要么i能整除它。

2.题目给了个N=2的样例,不太具有代表性,因此我列一个N=4的solution:

[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]

3.列出来之后,我的最直接的想法就是枚举法。题目要求 N <= 15 ,如果用纯粹的枚举,最多需要 15! 步。所以在枚举的过程中需要剪枝,就是列出之前先判断是否符合 两个要求之一 。这样其实可以省去特别多的次数。

4.实际写出来的代码如下:

#include <set>

class Solution {
public:
    int countArrangement(int N) {
        int count = 0;
        std::set<int> restNum;
        for (int i = 1; i <= N; i++)
            restNum.insert(i);
        recurse(1, restNum, count, N);
        return count;
    }

    /**
     ** @param 当前递归所在的数组下标 i
     ** @param 当前递归所剩余的未使用的数字 restNum
     ** @param beautiful arrangement的个数,按引用传递
     ** @param 最大数字 N
     */
    void recurse(int i, set<int> restNum, int& count, int N) {
        // 递归结束条件
        if (i == N) {
            int target = *restNum.begin();
            if (target % i == 0 || i % target == 0)
                count++;
            else
                return;
        }
        if (i < N) {
            std::set<int>::iterator it = restNum.begin();
            // 对于满足条件的数字进行枚举,依次放入
            for (; it != restNum.end(); it++) {
                int target = *it;
                if (target % i == 0 || i % target == 0) {
                    std::set<int> recurseSet(restNum);
                    recurseSet.erase(target);
                    recurse(i + 1, recurseSet, count, N);
                }
            }
        }
    }

};

5.以上代码已通过。一开始用的vector,写到一半觉得删除元素不方便,改成了set,果然顺手许多~我觉得应该有更好的做法,有大神的话希望分享一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值