选与不选?从暴力枚举到背包dp

很多时候我们都会遇到一些题目,给出很多个位置,在这个位置进行操作或者不进行操作,我们称之为选与不选问题

如果我们进行dfs进行枚举的话,复杂度可能吃不消,我们借鉴背包的思路,背包的思路本质就是枚举每一个物品选与不选


题目地址

在这里插入图片描述

这个题目我的定义就是 dp[ i ] [ j ] 为使用了i块地毯,到第 j 个位置能够覆盖的白色区域的最大数量,我省略了第一个维度的空间。
我们提前处理处理好白色数量的前缀和pre
注意的是,我们的转移方程是

d p [ j ] = m a x ( d p [ u ] 1 < = u < = ( j − c a r p e t L e n ) + p r e [ j ] − p r e [ j − c a r p e t L e n ] , d p [ j ] ) dp [ j ] = max( dp[ u ]_{1<= u <=(j-carpetLen)} + pre[j] - pre[j-carpetLen] , dp[ j ] ) dp[j]=max(dp[u]1<=u<=(jcarpetLen)+pre[j]pre[jcarpetLen],dp[j])

现在有几个难点就是如何快速算出 j 前面的最大值,以及每次算完后如何更新整个的答案区间呢

            for(int j=1;j<=n;j++){
                c[j] = max(c[j-1],dp[j]);   // 通过迭代的方式求出前面的最小值
            }
            for(int j=1;j<=n;j++){
                dp[j] = max(dp[j],dp[j-1]);  // 每次算完更新一下
            }

完整的代码如下:

class Solution {
public:
    int minimumWhiteTiles(string floor, int numCarpets, int carpetLen) {
        int n = floor.size();
        int ans = 0x3f3f3f3f;
        vector<int> pre(n+1,0);
        for(int i=0;i<n;i++){
            pre[i+1] = pre[i];
            if(floor[i]=='1'){
                pre[i+1] += 1;
            }
        }
        // 已经处理好了前缀和
        // 开始枚举
        vector<int> dp(n+1,0); // dp[i][j] 表示用i块地毯到第j个位置的覆盖的白色方块最大数
        // 先进行初始化
        int big = 0;
        for(int i = 1;i<=n;i++){
            if(i<=carpetLen){
                dp[i] = pre[i];
                big = max(big,dp[i]);
            }else{
                dp[i] = max(big,pre[i]-pre[i-carpetLen]);
                big = max(big,dp[i]);
            }
        }
        for(int i=2;i<=numCarpets;i++){
            // 枚举每一个位置
            // 提前找出每一个位置包含这个位置的的最大值
            vector<int> c(n+1,0);
            for(int j=1;j<=n;j++){
                c[j] = max(c[j-1],dp[j]);
            }
            for(int j=n;j>=(carpetLen);j--){
                dp[j] = max(c[j-carpetLen]+pre[j]-pre[j-carpetLen],dp[j]);
            }
            for(int j=1;j<=n;j++){
                dp[j] = max(dp[j],dp[j-1]);
            }
        }
        return pre[n] - dp[n];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wniuniu_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值