LeetCode 完全平方数(深度优先搜索、广度优先搜索、动态规划)

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

方法一:使用回溯法(深度优先)。从n的平方根到1进行搜索,直到当前的n为零。

class Solution {
private:
	int result;
    //在steps之后将n转变成reminaNum后,继续搜索
	void dfs(int reminaNum, int steps) {
		if (reminaNum == 0) {//如果正好匹配完成
			result = steps;//更新结果
			return;
		}
		else if (result > steps) {//剪枝(只有当前的步数少于当前已经搜索到的最小结果才有继续搜索的必要
			//sqrt(reminaNum)将reminaNum开方
			for (int num = sqrt(reminaNum); num > 0; --num) {
				//因为是steps最小,所以从大到小进行搜索
				dfs(reminaNum - num * num, steps + 1);
			}
		}
	}
public:
	int numSquares(int n) {
		result = INT_MAX;
		dfs(n, 0);//开始搜素
		return result;
	}
};

LeetCode 完全平方数(深度优先搜索、广度优先搜索、动态规划)_第1张图片
方法二:使用队列辅助进行广度优先搜索。

class Solution {
public:
	int numSquares(int n) {
		if (n == 0) {
			return 0;
		}
		int steps = 0, tempSize, tempValue;//steps是当前的步数
		queue myQue;//广度优先搜索的辅助队列
		myQue.push(n);
		while (!myQue.empty()) {
			tempSize = myQue.size();//当前队列的大小
			//当前队列中的状态都向后移动一步
			for (int i = 0; i < tempSize; ++i) {
				tempValue = myQue.front();//获取当前队头
				myQue.pop();
				//对tempValue进行尝试平方分解
				for (int j = sqrt(tempValue); j > 0; --j) {
					if (tempValue == j * j) {//第一次成功搜索到了即为答案
						return steps + 1;
					}
					else {
						myQue.push(tempValue - j * j);//否则放入移动之后的状态
					}
				}
			}
            steps += 1;//当前移动了一步
		}
		return 0;
	}
};

LeetCode 完全平方数(深度优先搜索、广度优先搜索、动态规划)_第2张图片
方法三:动态规划。

状态转移方程: dp[num] = min(dp[num], dp[num - j * j] + 1);
class Solution {
public:
	int numSquares(int n) {
		vector dp(n + 1, INT_MAX);//动态规划数组,dp[i]表示的i的完全平方数
		dp[0] = 0;
		for (int num = 1; num <= n; ++num) {
            //对num进行平方拼凑尝试
			for (int j = 1; j * j <= num; ++j) {
				dp[num] = min(dp[num], dp[num - j * j] + 1);
			}
		}
		return dp[n];
	}
};

LeetCode 完全平方数(深度优先搜索、广度优先搜索、动态规划)_第3张图片
方法四:评论区说是数序问题。
四平方定理: 任何一个正整数都可以表示成不超过四个整数的平方之和。
推论:满足四数平方和定理的数n(四个整数的情况),必定满足 n=4a(8b+7)

int numSquares(int n) {
    //先根据上面提到的公式来缩小n
    while(n % 4 == 0) {
        n /= 4;
    }
    //如果满足公式 则返回4
    if(n % 8 == 7) {
        return 4;
    }
    //在判断缩小后的数是否可以由一个数的平方或者两个数平方的和组成
    int a = 0;
    while ((a * a) <= n) {
        int b = sqrt((n - a * a));
        if(a * a + b * b == n) {
            //如果可以 在这里返回
            if(a != 0 && b != 0) {
                return 2;
            } else{
                return 1;
            }
        }
        a++;
    }
    //如果不行 返回3
    return 3;
}

你可能感兴趣的:(LeetCode)