数据结构学习日志——1.24、1.25(动态规划)

回顾一下分治思想,归并排序就是采用的这种思路:
https://leetcode-cn.com/problems/maximum-subarray/solution/zui-da-zi-xu-he-by-leetcode-solution/在这里插入图片描述
这段话很重要,其实浏览器的搜索方式就是依赖于树结构,快速找到关键词所在的位置和信息。

题目1:零钱兑换II
在这里插入图片描述
这道题的难点在于存在重复的情况,如1 2 1 1和1 1 2 1是属于同一种零钱兑换方式,因此不能简单的将dp[i]写成
  dp[i]+=dp[i-coin]
  注意这里的循环方式不能将状态i作为外层循环,而应该将coins作为外层循环,状态i作为内层循环,利用不同的coin来更新状态数组dp[i]
在这里插入图片描述
在这里插入图片描述
正确的循环代码如下:

for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

  仔细观察可以发现,加入我要计算dp[6],coins为[1,5],那么首先循环将coins[0]=1带入,在内层循环中可以得出
dp[6]=1(61),外层完成一次循环。开启第二次外循环coins[1]=5,在内循环中dp[6]更新为2(61 1+5)。然后就完全结束了,不会记录(5+1)的情况,即不会存在重复,内层循环代码从conins[i]开始,因为小于conins[i]的状态数组必定为0。此时dp[j]里算出来的就是组合数
  如果将两个循环交换一下:

for (int j = 0; j <= amount; j++) { // 遍历背包容量
    for (int i = 0; i < coins.size(); i++) { // 遍历物品
        if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
    }
}

背包容量的每一个值,都是经过 1 和 5 的计算,包含了[1, 5] 和 [5, 1]两种情况。此时dp[j]里算出来的就是排列数
在这里插入图片描述
详细讲解见链接:
https://leetcode-cn.com/problems/coin-change-2/solution/518-ling-qian-dui-huan-iiwan-quan-bei-ba-ynjf/

题目2:分割等和子集
在这里插入图片描述
这个是0-1背包问题,解题思路见链接:
https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
在这里插入图片描述
在这里插入图片描述
代码:

bool canPartition(vector<int>& nums) {
	//计算nums的和
	int sum = accumulate(nums.begin(), nums.end(), 0);
	//如果sum为奇数,则返回false,因为奇数必定不能整除一半
	if (sum & 1)	return false;
	//求出数组中的最大值
	int max_num = *max_element(nums.begin(), nums.end());
	int target = sum / 2;
	if (max_num > target)	return false;
	//创建状态数组
	vector<vector<bool>>dp(nums.size(), vector<bool>(sum / 2 + 1, false));
	//确定边界条件
	//dp[i][0]都为true
	//dp[0][nums[0]]为true
	for (int i = 0; i < nums.size(); i++) {
		dp[i][0] = true;
	}
	dp[0][nums[0]] = true;
	//开始遍历
	for (int i = 1; i < nums.size(); i++) {
		for (int j = 1; j <= sum / 2; j++) {
			if (j < nums[i]) {
				dp[i][j] = dp[i - 1][j];
			}
			else {
				dp[i][j] = dp[i - 1][j] || dp[i-1][j - nums[i]];
			}
		}
	}
	return dp[nums.size() - 1][sum / 2];
}

进行空间优化:
在这里插入图片描述
代码:

bool canPartition2(vector<int>& nums) {
	//计算nums的和
	int sum = accumulate(nums.begin(), nums.end(), 0);
	//如果sum为奇数,则返回false,因为奇数必定不能整除一半
	if (sum & 1)	return false;
	//求出数组中的最大值
	int max_num = *max_element(nums.begin(), nums.end());
	int target = sum / 2;
	if (max_num > target)	return false;
	//创建状态数组
	vector<bool>dp(target+1,false);
	dp[0] = true;
	dp[nums[0]] = true;
	//开始遍历
	for (int i = 1; i < nums.size(); i++) {
		for (int j = target; j >= nums[i]; j--) {
			if (nums[i] == target)
				return true;
			dp[j] = dp[j] || dp[j - nums[i]];
		}
	}
	return dp[target];
}

题目3:
在这里插入图片描述
这道题的关键在于自己动手推算,发现前几个数的规律,明确”3“的重要性,根据动态规划,某个大于4的数i都可以拆分成 3+ (i-3),所以dp[i]=dp[i-3]*3。由此可得所有数
方法一:动态规划 自底向上

    int cuttingRope(int n) {
        if(n<=3)    return n-1;
        vector<long>dp(n+1,1);
        dp[2]=2;
        dp[3]=3;
        dp[4]=4;
        for(int i=5;i<=n;i++){
            dp[i]=dp[3]*dp[i-3];
            dp[i]=dp[i]%10000;
        }
        return dp[n];
    }

方法二:自顶向下,空间优化

//优化空间
    int cuttingRope(int n) {
        if(n<=3)    return n-1;
        long result=1;
        while(n>4){
            result=(result*3)%1000000007;
            n-=3;
        }
        return (result*n)%1000000007;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值