leetcode369周赛

2919. 使数组变美的最小增量运算数

给你一个下标从 0 开始、长度为 n 的整数数组 nums ,和一个整数 k 。
你可以执行下述 递增 运算 任意 次(可以是 0 次):
从范围 [0, n - 1] 中选择一个下标 i ,并将 nums[i] 的值加 1 。
如果数组中任何长度 大于或等于 3 的子数组,其 最大 元素都大于或等于 k ,则认为数组是一个美丽数组 。
以整数形式返回使数组变为 美丽数组 需要执行的 最小 递增运算数。
子数组是数组中的一个连续非空元素序列。

动态规划 dp[i]表示选择第i个数字后需要最小递增运算数
则dp[i] = min(dp[i-1], dp[i-2], dp[i-3]) = max(nums[i] - k, 0);

class Solution {
public:
    long long dp[100005];
    long long minIncrementOperations(vector<int>& nums, int k) {
        memset(dp,0,sizeof dp);
        dp[0] = max(0,k - nums[0]);
        dp[1] = max(0,k - nums[1]);
        dp[2] = max(0,k - nums[2]);
        int n=nums.size();
        for(int i=3;i<n;i++){
            dp[i] = max(0,k - nums[i]) + min(min(dp[i-2], dp[i-1]), dp[i-3]);
        }
        return min(min(dp[n-2], dp[n-1]), dp[n-3]);
    }
};

可以使用O(1)的空间复杂度

class Solution {
public:
    // long long dp[100005];
    long long minIncrementOperations(vector<int>& nums, int k) {
        long long f0 = 0, f1 = 0, f2 = 0;
        int n=nums.size();
        for(int i=0;i<n;i++){
            long long inc = f2 ;
            f2 = max(0, k - nums[i]) + min(min(f0, f1), f2);
            f0 = f1, f1 = inc;
        }
        return min(min(f0, f1), f2);
    }
};

2920. 收集所有金币可获得的最大积分

节点 0 处现有一棵由 n 个节点组成的无向树,节点编号从 0 到 n - 1 。给你一个长度为 n - 1 的二维 整数 数组 edges ,其中 edges[i] = [ai, bi] 表示在树上的节点 ai 和 bi 之间存在一条边。另给你一个下标从 0 开始、长度为 n 的数组 coins 和一个整数 k ,其中 coins[i] 表示节点 i 处的金币数量。
从根节点开始,你必须收集所有金币。要想收集节点上的金币,必须先收集该节点的祖先节点上的金币。节点 i 上的金币可以用下述方法之一进行收集:

  1. 收集所有金币,得到共计 coins[i] - k 点积分。如果 coins[i] - k 是负数,你将会失去 abs(coins[i] - k) 点积分。
  2. 收集所有金币,得到共计 floor(coins[i] / 2) 点积分。如果采用这种方法,节点 i 子树中所有节点 j 的金币数 coins[j] 将会减少至 floor(coins[j] / 2) 。
    返回收集 所有 树节点的金币之后可以获得的最大积分。

方法1 记忆化搜索,自顶向下

class Solution {
public:
    const static int N=1e5+5;
    int h[N],e[N<<1],nx[N<<1];
    int cnt=1;
    void add(int a,int b){
        e[cnt] = b, nx[cnt] = h[a], h[a] = cnt++;
    }
    vector<int> c;
    int k1;
    int dp[N][15];
    int dfs(int u,int f,int i){
        if(dp[u][i]!=-1)return dp[u][i];
        if(i>=14)return 0;
        int ans0 = (c[u]>>i) - k1, ans1 = c[u]>>(i+1);
        for(int j=h[u];j;j=nx[j]){
            int t=e[j];
            if(t!=f){
                ans0 += dfs(t, u, i);
                ans1 += dfs(t, u, i+1);
            }
        }
        
        return dp[u][i] = max(ans0, ans1);
    }
    int maximumPoints(vector<vector<int>>& edges, vector<int>& coins, int k) {
        memset(h,0,sizeof h);
        memset(e,0,sizeof e);
        memset(nx,0,sizeof nx);
        memset(dp,-1,sizeof dp);
        c = coins;
        k1 = k;
        for(int i=0;i<edges.size();i++){
            int a = edges[i][0], b = edges[i][1];
            add(a, b);
            add(b, a);
        }
        return dfs(0,-1,0);
    }
};

方法二 树状dp, 顶底向上

class Solution {
public:
    const static int N=1e5+5;
    int h[N],e[N<<1],nx[N<<1];
    int cnt=1;
    void add(int a,int b){
        e[cnt] = b, nx[cnt] = h[a], h[a] = cnt++;
    }
    vector<int> c;
    int k1;
    int dp[N][15][2];
    void dfs(int u,int f){
        for(int i=0;i<15;i++){
            dp[u][i][0] = (c[u]>>i) - k1;
            dp[u][i][1] = c[u]>>(i+1);
        }
        for(int j=h[u];j;j=nx[j]){
            int t=e[j];
            if(t!=f){
                dfs(t,u);
                for(int i=0;i<14;i++){
                    dp[u][i][0] += max(dp[t][i][0], dp[t][i][1]);
                    dp[u][i][1] += max(dp[t][i+1][0], dp[t][i+1][1]);
                }
            }
        }
    }
    int maximumPoints(vector<vector<int>>& edges, vector<int>& coins, int k) {
        memset(h,0,sizeof h);
        memset(e,0,sizeof e);
        memset(nx,0,sizeof nx);
        memset(dp,0,sizeof dp);
        c = coins;
        k1 = k;
        for(int i=0;i<edges.size();i++){
            int a = edges[i][0], b = edges[i][1];
            add(a, b);
            add(b, a);
        }
        dfs(0,-1);
        return max(dp[0][0][0],dp[0][0][1]);
    }
};

比赛过程中使用树状dp,一直设置二维dp[N][15], 发现无法做,需要设置三维dp[N][15][2],第二维记录前面已经进行多少次2操作,第三维记录当前选择1操作还是2操作,这和以往的树形dp不太一样,通常树形dp是父节点的操作不会影响子节点,这里会影响,所以要多设置一维记录上面节点的2操作影响,1操作不会影响,所以不用记录。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qq_43983809

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

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

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

打赏作者

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

抵扣说明:

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

余额充值