LeetCode---397周赛

题目列表

3146. 两个字符串的排列差

3147. 从魔法师身上吸取的最大能量

3148. 矩阵中的最大得分

3149. 找出分数最低的排列

一、两个字符串的排列差

简单的模拟题,只要统计出字符串出现的下标,然后相减求和即可,代码如下

class Solution {
public:
    int findPermutationDifference(string s, string t) {
        int cnt[26]{};
        int n = s.size();
        for(int i=0;i<n;i++){
            cnt[s[i]-'a']=i;
        }
        int ans = 0;
        for(int i=0;i<n;i++){
            int idx = t[i] - 'a';
            ans += abs(cnt[idx]-i);
        }
        return ans;
    }
};

二、从魔法师身上吸取的最大能量

这题同样是模拟题,题目要求每次固定向后跳跃k次,我们可以以不同的下标作为起点,往后面跳跃,计算出所有可能结果的最大值,时间复杂度为O(k*n),显然是过不了的。

如何优化时间复杂度?根据上面说的思路,我们会发现,当起点下标>=k时,我们就会开始重复的遍历之前已经遍历过的下标

那有没有方法让我们在一次遍历的过程中就能得到最优解,不用再去重复遍历呢?当然是有的,每到我们遍历到一个下标时,我们都有两者选择:1、继承从前面跳跃得到的能量继续往后跳,2、舍弃之前跳跃得到的能量,以当前下标为起始位置开始往后跳跃获得能量。我们只要取两者的最大值就行。(本质就是dp问题,和53. 最大子数组和类似,唯一的不同就在于我们是每隔k个选一个数)

代码如下

class Solution {
public:
    int maximumEnergy(vector<int>& energy, int k) {
        int n = energy.size();
        int ans = INT_MIN;
        for(int i = 0; i < k; i++){
            int mx = 0;
            for(int j=i;j<n;j+=k){
                mx = max(mx,0) + energy[j];
            }
            ans = max(ans,mx);
        }
        return ans;
    }
};

三、矩阵中的最大得分

这题要理解题意,其实也不是很难,只要举几个栗子,找到关键点即可。这里我们就拿第一个示例举例说明,我们按照它给的移动方法手动计算一下,7 - 5 + 14 - 7 = 14 - 5 = 9,就会发现我们计算的得分=未位置的值 - 初位置的值。

要求最大值,就是让末位置的值最大,初位置的值最小,同时要考虑到两个点的位置关系,如何做?我们只要枚举末位置,找以它未右下角的矩阵中的最小值作为初位置的值,求它们的最大值即可。

如何求二维数据中的最小值?和求解二维前缀和类似

代码如下

class Solution {
public:
    int maxScore(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<int>>f(n+1,vector<int>(m+1,INT_MAX));
        int ans = INT_MIN;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                f[i+1][j+1]=min(f[i+1][j],f[i][j+1]);
                ans = max(ans,grid[i][j]-f[i+1][j+1]);// 注意起始位置和末位置不能相同,所以这里算得分时,没有将grid[i][j]加入矩阵最小值中
                f[i+1][j+1]=min(f[i+1][j+1],grid[i][j]);
            }
        }
        return ans;
    }
};

四、找到分数最低的排列

这题就是暴力枚举所有可能的排列顺序,找到最小得分,然后倒过来找到达最小得分的排序,可以用状态压缩进行优化。思路不难想,主要是代码比较难写,可以看看下面的代码。

代码如下

class Solution {
public:
    vector<int> findPermutation(vector<int>& nums) {
        int n = nums.size();
        int memo[1<<n][n];
        memset(memo,-1,sizeof(memo));
        // 两个参数,mask记录哪些数字被选过(用二进制0/1表示是否被选过---状态压缩),pre记录前一个被选的数字
        function<int(int,int)>dfs=[&](int mask,int pre)->int{
            if(mask==(1<<n)-1){
                return abs(pre-nums[0]);
            }
            if(memo[mask][pre]!=-1) 
                return memo[mask][pre];
            int res = INT_MAX;
            for(int k=1;k<n;k++){
                if((mask>>k) & 1) continue;
                res = min(res,dfs(mask|1<<k,k)+abs(pre-nums[k]));
            }
            return memo[mask][pre]=res;
        };
        dfs(1,0);
        vector<int> ans;
        // 倒过来找排列顺序,由于路径是确定,所以该递归函数的时间复杂度为O(n)
        function<void(int,int)>get_ans=[&](int mask,int pre){
            ans.push_back(pre);
            if(mask==(1<<n)-1)
                return;
            int final_res = dfs(mask,pre);
            for(int k=1;k<n;k++){
                if((mask>>k) & 1) continue;
                if(dfs(mask|1<<k,k)+abs(pre-nums[k])==final_res){
                    get_ans(mask|1<<k,k);
                    break;
                }
            }
        };
        get_ans(1,0);
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值