11.19

彩笔表妹的DP之旅

换硬币

题目:给你n种面值的硬币(比如2元,5元,10元)(不要问我为什么有10元的硬币)和一定数量的钱m,求可以换成功的方法数
其实这个问题我上半年复试的时候有点印象,然后我今天又有幸找到了之前的视频重温了一下。视频中的题目问的是求交换成功的最少硬币的数量,我寻思这整挺好啊,换汤不换药。视频中的代码为:

dp[0]=0  
for(i=1;i<=M;++i){   //i为从1开始遍历金钱数
	dp[i]=Integer.MAX_VALUE;  //原题求的是最小硬币数,初始化的时候自然等于正无穷大了
	for(j=0;j<n;++j){ //遍历每种硬币种类
		if(i>=A[j]&&dp[i-A[j]]!=Integer.MAX_VALUE){
			dp[i]=Math.min(dp[i],1+dp[i-A[j]]);
			}
		}
	}
	if(dp[M]==Integer.MAX_VALUE){   //这种情况说明找不到能够交换的方法
		f[M]=-1
	}
	return f[M]

不知道下次看的时候能不能看懂哈。拿上面的例子,大概意思就是这个硬币交换的最后一步到底是交换2元,还是交换5元,还是交换10元呢?然后如果钱能够交换2元的话就继续DP。
针对问题的不同,我做了如下改动:
1.dp[0]=1,意思是能够取整,那么方法数+1
2.不整maxvalue了,都变成0
3.递推公式变成dp(m)=dp(m)+dp(m-A[i])
但是实际运算时发现操作有冗余!
20+5和5+20被计算成了两种方法!
原因是原题求的是最小硬币数量,它是不在乎多算几次的。多算几次并不会改变结果;而我们算的是方法数,多算几次问题就大了!
查阅其他CSDN的代码我知道了解决的方法,就是调换Coin和Money的遍历顺序!其实很神奇。这么做的目的在于使交换方法呈现出一个非递减的顺序,这里贴一个详细解释。
在这里插入图片描述
上图中的例子使coin={1,5},money=6的情形。
学会了🐎

求最小重量差

题目:给定一组石头重量序列,把石头分成两堆使重量差最小。
其实这题的一个难点就是如何造DP数组。肉眼可见要造二维的DP,其中一维是遍历石头,我想了半天没找到合理的另一维以及使这两维联系在一起的思路。(又)呜呜!看了解答才发现另一维是重量。一个没想过的思路:重量差最小,则两边都要尽可能接近总重量的一半,这就变成了一个变体的0-1背包的问题!
:给定物品的重量序列,求不超过总重量一半的最大分法(重量)。
主体代码如下:

for (int i = 1; i <= len; ++i) {    //i为石头个数
        for (int j = 1; j <= sum / 2; ++j) {   ///j从1-总重量的一半遍历
            if(j>=w[i-1])
            	dp[i][j] = max(dp[i-1][j],w[i-1]+dp[i-1][j-w[i-1]]);
            else 
            	dp[i][j] = dp[i - 1][j];
        }
    }

其实后面还有一个不错的小trick,返回最小重量差不要再返回去比大小啥的。只需要返回 w e i g h t − 2 ∗ d p [ i ] [ w e i g h t / 2 ] weight-2*dp[i][weight/2] weight2dp[i][weight/2]就可以了。
因为 d p [ i ] [ w e i g h t / 2 ] dp[i][weight/2] dp[i][weight/2]求的是较轻的那方不超过 w e i g h t / 2 weight/2 weight/2的重量,然后 w e i g h t / 2 weight/2 weight/2又是中心轴。品一品。(dp=i,i+j=weight,求j-i=weight-2*dp)

就写到这吧,不知道明天的OJ测试题会不会记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值