1N 皇后 II
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:

输入:n = 4 输出:2 解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1 输出:1
提示:
- 1 <= n <= 9
思路:
-  回溯算法: - 使用回溯算法探索所有可能的解空间。从棋盘的第一行开始,逐行尝试在每一列放置皇后,并检查是否满足 N 皇后问题的要求。
- 如果当前位置可以放置皇后,则递归到下一行,继续尝试放置下一个皇后。
- 如果某一行无法放置皇后,或者已经放置了 N 个皇后,尝试改变之前的放置,进行回溯。
 
-  isValid 函数: - isValid函数用于检查在给定位置放置皇后是否合法,即是否满足 N 皇后问题的要求。
- 检查条件包括:列上是否有皇后、左上方是否有皇后、右上方是否有皇后。
 
-  总结解的数量: - 使用一个私有变量 count记录符合条件的解的数量。
- 每当成功放置 N 个皇后,即遍历完所有行后,增加 count的值。
 
- 使用一个私有变量 
-  totalNQueens 函数: - totalNQueens函数作为入口点,初始化一个大小为 N x N 的棋盘,并调用- backtracking函数开始搜索解空间。
- 最终返回符合条件的解的数量。
 
代码:
class Solution {
private:
int count = 0;
void backtracking(int n, int row, vector<string>& chessboard) {
    if (row == n) { // 如果已经遍历完所有行,表示找到了一个解
        count++;
        return;
    }
    for (int col = 0; col < n; col++) {
        if (isValid(row, col, chessboard, n)) {
            chessboard[row][col] = 'Q'; // 放置皇后
            backtracking(n, row + 1, chessboard); // 继续下一行的放置
            chessboard[row][col] = '.'; // 回溯,尝试其他位置
        }
    }
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    for (int i = 0; i < row; i++) { // 检查列是否有皇后
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q') { // 检查左上方是否有皇后
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') { // 检查右上方是否有皇后
            return false;
        }
    }
    return true; // 该位置可以放置皇后
}
public:
    int totalNQueens(int n) {
        std::vector<std::string> chessboard(n, std::string(n, '.')); // 初始化棋盘
        backtracking(n, 0, chessboard); // 回溯搜索解空间
        return count; // 返回解的个数
    }
};2分割回文串 II
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是
回文串
。
返回符合要求的 最少分割次数 。
示例 1:
输入:s = "aab" 输出:1 解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例 2:
输入:s = "a" 输出:0
示例 3:
输入:s = "ab" 输出:1
提示:
- 1 <= s.length <= 2000
- s仅由小写英文字母组成
思路:
- 构建一个二维数组 isPalindromic,用来记录字符串 s 中每个子串是否为回文串。
- 遍历字符串 s,填充 isPalindromic 数组,判断每个子串是否为回文串。
- 初始化一个一维数组 dp,用来记录从第一个字符到当前位置字符所需的最小切割次数,初始值为每个位置的索引值。
- 动态规划计算最小切割次数: 
  - 如果当前位置到第一个位置的子串本身就是回文串,则该位置的最小切割次数为 0。
- 否则,在前面的子串中找到最小切割次数,并更新当前位置所需的最小切割次数。
 
- 返回 dp 数组中最后一个位置的值,即从第一个字符到最后一个字符的最小切割次数
代码:
class Solution {
public:
    int minCut(string s) {
        // 初始化一个二维数组,用来存储从位置 i 到 j 的子串是否是回文串
        vector<vector<bool>> isPalindromic(s.size(), vector<bool>(s.size(), false));
        
        // 填充 isPalindromic 数组
        for (int i = s.size() - 1; i >= 0; i--) {
            for (int j = i; j < s.size(); j++) {
                // 判断 s[i] 到 s[j] 是否是回文串
                if (s[i] == s[j] && (j - i <= 1 || isPalindromic[i + 1][j - 1])) {
                    isPalindromic[i][j] = true;
                }
            }
        }
        
        // 初始化一个一维数组 dp,用来记录从第一个字符到第 i 个字符需要的最小切割次数
        vector<int> dp(s.size(), 0);
        for (int i = 0; i < s.size(); i++) {
            dp[i] = i; // 初始值设为 i
        }
        // 动态规划计算最小切割次数
        for (int i = 1; i < s.size(); i++) {
            // 如果从第一个字符到第 i 个字符是回文串,最小切割次数为 0
            if (isPalindromic[0][i]) {
                dp[i] = 0;
                continue;
            }
            // 在不是回文串的情况下,循环遍历前面的子串找到最小切割次数
            for (int j = 0; j < i; j++) {
                if (isPalindromic[j + 1][i]) {
                    // 更新最小切割次数为当前位置与前一个回文串位置的切割次数加 1
                    dp[i] = min(dp[i], dp[j] + 1);
                }
            }
        }
        return dp[s.size() - 1]; // 返回从第一个字符到最后一个字符的最小切割次数
    }
};3最长递增子序列的个数
给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。
注意 这个数列必须是 严格 递增的。
示例 1:
输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输入: [2,2,2,2,2] 输出: 5 解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
提示:
- 1 <= nums.length <= 2000
- -106 <= nums[i] <= 106
思路:
-  定义状态: 定义两个数组 dp和count,其中dp[i]表示以第i个元素结尾的最长递增子序列的长度,count[i]表示以第i个元素结尾的最长递增子序列的个数。
-  状态转移方程: 对于每个元素 nums[i],遍历其之前的元素nums[j](其中j < i),如果nums[i] > nums[j],则:- 如果 dp[j] + 1 > dp[i],则更新dp[i] = dp[j] + 1,并且count[i]更新为之前的count[j]。
- 如果 dp[j] + 1 == dp[i],则累加count[i] += count[j]。
 
- 如果 
-  计算最终结果: 找到最长递增子序列的长度 maxCount,然后遍历dp数组,将所有长度为maxCount的递增子序列的个数相加,得到最终的结果。
代码:
class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        // 如果数组长度小于等于1,直接返回数组长度
        if (nums.size() <= 1) return nums.size();
        
        // 初始化动态规划数组 dp 和计数数组 count
        vector<int> dp(nums.size(), 1); // dp[i] 表示以第 i 个元素结尾的最长递增子序列的长度
        vector<int> count(nums.size(), 1); // count[i] 表示以第 i 个元素结尾的最长递增子序列的个数
        
        int maxCount = 0; // 用于记录最长递增子序列的长度
        
        // 遍历数组进行动态规划
        for (int i = 1; i < nums.size(); i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    // 更新 dp 和 count
                    if (dp[j] + 1 > dp[i]) { // 如果能取得更长的递增子序列
                        dp[i] = dp[j] + 1;
                        count[i] = count[j]; // 更新计数为之前的 count[j]
                    } else if (dp[j] + 1 == dp[i]) {
                        // 如果有相同长度的递增子序列出现,累加计数
                        count[i] += count[j];
                    }
                }
                
                // 更新最长递增子序列的长度
                if (dp[i] > maxCount) maxCount = dp[i];
            }
        }
        
        int result = 0; // 最终结果
        
        // 统计最长递增子序列的个数
        for (int i = 0; i < nums.size(); i++) {
            if (maxCount == dp[i]) result += count[i];
        }
        
        return result;
    }
};


















