4月28日

第一题Leetcode51https://leetcode.cn/problems/n-queens/?envType=daily-question&envId=2025-04-284月28日_第1张图片

代码: 

class Solution {
public:
    int N;
    string position={".........."};
    int c[11]={0}, p[22] = { 0 }, q[20] = { 0 };
    vector save;
    vector> ans;
    
    void dfs(int i) {
	if (i > N) {
        ans.push_back(save);
		return;
	}
    //进入下一层初始化
    for(int i=0;i<=N;i++){
            position[i]='.';
        }
	for (int j = 1;j <= N;j++) {
		if(c[j]||p[i+j]||q[i-j+N]) continue;//若该位置被占领,向下一列尝试
		position[j] = 'Q';
		c[j] = 1;//标记占领列
		p[i + j] = 1;//标记占领撇
		q[i - j + N] = 1;//捺
        save.push_back(position.substr(1,N));

		dfs(i + 1);//下一层
        //恢复现场
        save.pop_back();
		c[j] = 0;
		p[i + j] = 0;
		q[i - j + N] = 0;
        position[j]='.';
	    }
    }

    vector> solveNQueens(int n) {
        N=n;
        dfs(1);
        return ans;
    }
};

解题思路:

        这题整体框架与N皇后||是相同的,因此我们可以取用N皇后||代码改造,N皇后||题解->https://blog.csdn.net/2403_89280420/article/details/147366439?spm=1001.2014.3001.5501 。

         整体框架依然是递归回溯,不过题目要求我们返回由二维动态数组存储的地图,那么我们就要在每一次枚举时更新当前地图。

(1)核心思想

  • 逐行放置皇后:从第 1 行到第 N 行,依次尝试在每一行的某个列放置皇后。

  • 剪枝优化:通过标记数组快速判断当前位置是否会被其他皇后攻击,避免无效搜索。

  • 回溯恢复:如果当前路径无法找到解,撤销最近的选择(即移除皇后),并尝试其他位

(2)关键变量说明

变量名 作用
N 棋盘大小(N×N)。
position 表示当前行的皇后摆放状态(如 ".Q.."),初始化为 ".........."(10个点)。
c 标记列是否被占用(c[j] = 1 表示第 j 列已有皇后)。
p 标记主对角线(从左上到右下)是否被占用(p[i + j] = 1)。
q 标记副对角线(从右上到左下)是否被占用(q[i - j + N] = 1)。
save 存储当前棋盘状态(每行的摆放情况)。最深处时存储该路径地图状态。
ans 存储所有合法的解。

(3) 算法流程

  1. 初始化

    • 在 solveNQueens 中传入 N = n,并启动回溯函数 dfs(1)(从第 1 行开始)。

  2. 回溯函数 dfs(i)

    • 终止条件:如果 i > N,说明所有行都成功放置皇后,即进行到最深层,将当前棋盘状态 save 加入 ans

    • 尝试每一列

      • 检查第 j 列是否被占用(c[j])、主对角线(p[i + j])、副对角线(q[i - j + N])。

      • 如果当前位置合法:

        1. 放置皇后:position[j] = 'Q',并更新标记数组 cpq

        2. 保存当前行状态:save.push_back(position.substr(1, N))(截取有效部分)。

        3. 递归处理下一行:dfs(i + 1)

        4. 回溯:恢复标记数组和 position,尝试其他列。

  3. 输出结果ans 中存储了所有合法的棋盘布局。

        时间复杂度最坏为O(n!),空间复杂度为O(n)。

考察点:

  1. 递归回溯复原-每一层地图
  2. 边界处理与全局变量管理

第二题:Leetcode3326https://leetcode.cn/problems/minimum-division-operations-to-make-array-non-decreasing/

4月28日_第2张图片代码:

class Solution {
public:
    int times;
    int front;

    void findtime(int a) {
    int factor = Find(a); // 找到最大真因数
    if (factor && a / factor > front) {
        times++;
        findtime(a / factor);
    } else {
        if (factor == 0) { // 无法找到真因数
            times = 0;
            return;
        }
        times++;
        front = a / factor; // 更新 front
        return;
    }
}
    // 找到最大真因数
    int Find(int a) {
    if (a % 2 == 0) return a / 2; // 先检查 2
    for (int p = 3; p * p <= a; p += 2) { // 只检查奇数因数
        if (a % p == 0) return a / p;
    }
    return 0; // 无真因数
}
    int minOperations(vector& nums) {
        int alltimes=0;
        int n=nums.size();
        if(n==1)return 0;
        int found=0;
        front=nums[n-1];
        for(int i=n-2;i>=0;i--){
            times=0;
            //front=nums[i+1];
            if(nums[i]<=front){front=nums[i];continue;}//已经小于等于前一位的,直接continue
            findtime(nums[i]);//times用于判断该数能否通过除最大真因数小于nums[i+1]
            if(!times)return -1;//如果不能,直接返回-1
            alltimes+=times;//如果可以,累加
        }
        return alltimes;
    }
};

解题思路:

        要使得数组变成递增或不增的,那么数组的最大一位就是整个数组的最大值。所以我们从后向前遍历,这样更容易操作每个数的最大值。并确保其不大于后一个值。

        对于每个大于当前后位值的元素,我们递归地将其分解为最大真因数,直到它小于或等于当前最小值,同时记录操作次数。如果遇到一个元素无法分解(即它是质数)且仍然大于当前最小值,则整个问题无解,返回 -1。

  1. 初始化:从数组的倒数第二个元素开始向前遍历。

  2. 比较与分解:对于每个元素,如果它大于后一位的值,则递归地分解它,直到它小于或等于后位值。

  3. 记录操作次数:每次分解操作都会增加操作次数。

  4. 更新最小值:每次成功分解后,更新当前的最小值为分解后的结果。

  5. 检查无解情况:如果在分解过程中遇到无法分解的元素(质数),则直接返回 -1。

函数解释:

  • findtime函数:递归地分解给定的数,直到它小于或等于 front(当前最小值),并记录操作次数。

  • Find函数:找到一个数的最大真因数,如果没有则返回 0。

        时间复杂度O(n*m),空间复杂度O(1)。n为数组长度,m为操作次数。

考察点:

  1. 贪心算法:每次变小时需要选择最大真因数。
  2. 数学思维:如何每次查找最大真因数(Find函数)。
  3. 如何递归连续分解数(findtime函数)。

你可能感兴趣的:(深度优先,算法)