力扣9.29

2073. 买票需要的时间

n 个人前来排队买票,其中第 0 人站在队伍 最前方 ,第 (n - 1) 人站在队伍 最后方 。

给你一个下标从 0 开始的整数数组 tickets ,数组长度为 n ,其中第 i 人想要购买的票数为 tickets[i]

每个人买票都需要用掉 恰好 1 秒 。一个人 一次只能买一张票 ,如果需要购买更多票,他必须走到 队尾 重新排队(瞬间 发生,不计时间)。如果一个人没有剩下需要买的票,那他将会 离开 队伍。

返回位于位置 k(下标从 0 开始)的人完成买票需要的时间(以秒为单位)。

数据范围

  • n == tickets.length
  • 1 <= n <= 100
  • 1 <= tickets[i] <= 100
  • 0 <= k < n

分析

首先将第k个人移到尾部,并更新初始条件,此时只需要找每一轮剩下的人数,由于全都在第k个人前面,因此每一轮答案加上的权重就是剩下的人数,可以排序,每次找剩下的人中票数最小的人群,然后在答案一次性加上他们所贡献的时间并删除他们

代码

class Solution {
public:
    int timeRequiredToBuy(vector<int>& tickets, int k) {
        int n = tickets.size();
        vector<int> tmp;
        int res = 0;
        res += k + 1;
        for(int i = 0; i <= k; i ++ ) tickets[i] -- ;
        int cnt = 0;
        for(int i = 0; i < tickets.size(); i ++ ) {
            tmp.push_back(tickets[i]);
        }
        int o = tickets[k];
        sort(tmp.begin(), tmp.end());
        for(int i = 0; i < tmp.size(); i ++ ) {
            int last = 1, j = i + 1;
            while(j < tmp.size() && tmp[j] == tmp[i]) {
                last ++ ;
                j ++ ;
            }
            res += (n - i) * (min(o, tmp[i]) - cnt); // 剩下的人数×轮数
            cnt = max(cnt, min(tmp[i], o));
            i = j - 1;
        }
        return res;
    }
};

2320. 统计放置房子的方式数

一条街道上共有 n * 2 个 地块 ,街道的两侧各有 n 个地块。每一边的地块都按从 1n 编号。每个地块上都可以放置一所房子。

现要求街道同一侧不能存在两所房子相邻的情况,请你计算并返回放置房屋的方式数目。由于答案可能很大,需要对 109 + 7 取余后再返回。

注意,如果一所房子放置在这条街某一侧上的第 i 个地块,不影响在另一侧的第 i 个地块放置房子。

数据范围

  • 1 <= n <= 104

分析

状态机DP,令 d p [ i ] [ 1 ] dp[i][1] dp[i][1]表示处理完前i个且防止房子在i列第一排的方案数,同理定义 d p [ i ] [ 0 ] dp[i][0] dp[i][0] d p [ i ] [ 2 ] dp[i][2] dp[i][2] d p [ i ] [ 3 ] dp[i][3] dp[i][3]
状态转移如下:

  • d p [ i ] [ 0 ] = ( d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] + d p [ i − 1 ] [ 3 ] ) ; dp[i][0] = (dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][3]) ; dp[i][0]=(dp[i1][0]+dp[i1][1]+dp[i1][2]+dp[i1][3]);
  • d p [ i ] [ 1 ] = ( d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 2 ] ) ; dp[i][1] = (dp[i - 1][0] + dp[i - 1][2]) ; dp[i][1]=(dp[i1][0]+dp[i1][2]);
  • d p [ i ] [ 2 ] = ( d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] ) ; dp[i][2] = (dp[i - 1][0] + dp[i - 1][1]) ; dp[i][2]=(dp[i1][0]+dp[i1][1]);
  • d p [ i ] [ 3 ] = d p [ i − 1 ] [ 0 ] ; dp[i][3] = dp[i - 1][0] ; dp[i][3]=dp[i1][0];

代码

typedef unsigned long long ULL;
class Solution {
public:
    const static int N = 1e4 + 5, mod = (int)1e9 + 7;
    ULL dp[N][4];
    int countHousePlacements(int n) {
        dp[0][0] = 1;
        for(int i = 1; i <= n; i ++ ) {
            dp[i][0] = (dp[i - 1][0] % mod + dp[i - 1][1] % mod + dp[i - 1][2] % mod + dp[i - 1][3] % mod) % mod;
            dp[i][1] = (dp[i - 1][0] % mod + dp[i - 1][2] % mod) % mod;
            dp[i][2] = (dp[i - 1][0] % mod + dp[i - 1][1] % mod) % mod;
            dp[i][3] = dp[i - 1][0] % mod;
            
        }
        ULL res = 0;
        for(int i = 0; i < 4; i ++ ) {
            res += dp[n][i];
            res %= mod;
        }
        return res;
    }
};

213. 打家劫舍 II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

数据范围

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

分析

分两种情况讨论,对于第一个房子选择的情况,dp范围为1-n-1,对于第一个房子不选择的情况,dp范围为2-n,具体状态转移看代码

代码

class Solution {
public:
    const static int N = 105;
    int dp1[N][2], dp2[N][2];
    int rob(vector<int>& nums) {
        if(nums.size() == 1) return nums[0];
        int n = nums.size();
        for(int i = 1; i <= n - 1; i ++ ) { // 选择第一个
            dp1[i][0] = max(dp1[i - 1][1], dp1[i - 1][0]);
            dp1[i][1] = dp1[i - 1][0] + nums[i - 1];
        }
        for(int i = 2; i <= n; i ++ ) { // 不选择第一个
            dp2[i][0] = max(dp2[i - 1][1], dp2[i - 1][0]);
            dp2[i][1] = dp2[i - 1][0] + nums[i - 1];
        }
        int res = 0;
        res = max(dp1[n - 1][0], max(dp1[n - 1][1], max(dp2[n][0], dp2[n][1])));
        return res;
    }
};

3186. 施咒的最大总伤害

一个魔法师有许多不同的咒语。

给你一个数组 power ,其中每个元素表示一个咒语的伤害值,可能会有多个咒语有相同的伤害值。

已知魔法师使用伤害值为 power[i] 的咒语时,他们就 不能 使用伤害为 power[i] - 2power[i] - 1power[i] + 1 或者 power[i] + 2 的咒语。

每个咒语最多只能被使用 一次 。

请你返回这个魔法师可以达到的伤害值之和的 最大值 。

数据范围

  • 1 <= power.length <= 105
  • 1 <= power[i] <= 109

分析

使用 m a p map map进行 d p dp dp,首先记录每个值出现的次数,然后 p r e pre pre数组记录比当前值小的数的值,状态转移同打家劫舍,也是个状态机DP,最后极限过

代码

typedef unsigned long long ULL;
class Solution {
public:
    const static int N = 1e5 + 5;
    map<ULL, ULL> pre;
    map<ULL, ULL> dp[2];
    map<ULL, ULL> mp;
    ULL maximumTotalDamage(vector<int>& power) {
        sort(power.begin(), power.end());
        int n = power.size();
        int last = -1;
        ULL maxx = 0;
        for(int i = 0; i < n; i ++ ) {
            maxx = max(maxx, (ULL)power[i]);
            int j = i;
            while(j < n && power[j] == power[i]) {
                mp[power[i]] ++ ;
                j ++ ;
            }
            pre[power[i]] = last;
            last = power[i];
            i = j - 1;
        }
        for(auto [k, v] : mp) {
            if(mp[k - 1] && mp[k - 2]) {
                int last = pre[pre[pre[k]]];
                dp[0][k] = max(dp[1][k - 1], dp[0][k - 1]);
                dp[1][k] = max(dp[0][last], dp[1][last]) + v * k;
            } else if(mp[k - 1] && !mp[k - 2]) {
                int last = pre[pre[k]];
                dp[0][k] = max(dp[0][k - 1], dp[1][k - 1]);
                dp[1][k] = max(dp[0][last], dp[1][last]) + v * k;
            } else if(!mp[k - 1] && mp[k - 2]) {
                int last = pre[pre[k]];
                dp[0][k] = max(dp[0][k - 2], dp[1][k - 2]);
                dp[1][k] = max(dp[0][last], dp[1][last]) + v * k;
            } else {
                int last = pre[k];
                dp[0][k] = max(dp[0][last], dp[1][last]);
                dp[1][k] = max(dp[0][last], dp[1][last]) + v * k;
            }
        }
        return max(dp[0][maxx], dp[1][maxx]);
    }
};

2606. 找到最大开销的子字符串

给你一个字符串s ,一个字符 互不相同 的字符串 chars 和一个长度与 chars 相同的整数数组 vals

子字符串的开销 是一个子字符串中所有字符对应价值之和。空字符串的开销是 0 。

字符的价值 定义如下:

  • 如果字符不在字符串 chars 中,那么它的价值是它在字母表中的位置(下标从 1 开始)。
    • 比方说,'a' 的价值为 1'b' 的价值为 2 ,以此类推,'z' 的价值为 26
  • 否则,如果这个字符在 chars 中的位置为 i ,那么它的价值就是 vals[i]
    请你返回字符串 s 的所有子字符串中的最大开销。

数据范围

  • 1 <= s.length <= 105
  • s 只包含小写英文字母。
  • 1 <= chars.length <= 26
  • chars 只包含小写英文字母,且 互不相同 。
  • vals.length == chars.length
  • -1000 <= vals[i] <= 1000

分析

d p [ i ] dp[i] dp[i]表示已 i i i为结尾子串的价值最大值,状态转移如下

  • d p [ i ] = m a x ( 0 , d p [ i − 1 ] + k ) , k 为价值 dp[i]=max(0,dp[i-1]+k),k为价值 dp[i]=max(0,dp[i1]+k)k为价值

代码

class Solution {
public:
    map<char, int> pos;
    const static int N = 1e5 + 5;
    int dp[N];
    int maximumCostSubstring(string s, string chars, vector<int>& vals) {
        for(int i = 0; i < chars.size(); i ++ ) pos[chars[i]] = i;
        int res = 0;
        for(int i = 1; i <= s.size(); i ++ ) {
            int k = 0;
            if(pos.find(s[i - 1]) != pos.end()) k = vals[pos[s[i - 1]]];
            else k = s[i - 1] - 'a' + 1;
            dp[i] = max(0, dp[i - 1] + k);
            res = max(res, dp[i]);
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值