文章目录

1. 比赛结果

通过了 3 题,第245名,进入复赛了,收获 T恤 一件,哈哈。
阿里云 超级码力在线编程大赛初赛 第1场(第245名)-LMLPHP
阿里云 超级码力在线编程大赛初赛 第1场(第245名)-LMLPHP

2. 题目

1. 树木规划

题目链接

描述

在一条直的马路上,有 n 棵树,每棵树有一个坐标,代表它们距离马路起点的距离。 如果每相邻的两棵树之间的间隔不小于 d,那么我们认为这些树是美观的。 请计算出最少移除多少棵树,可以让这些树变得美观。

树木的棵树为 n,1 <= n <= 10^5。 树木的坐标用 trees 表示,0 <= trees[i] <= 10^9。 最小美观间隔为 d,1 <= d <= 10^9。 保证输入的坐标是排好序的,且两两不相同。

说明

样例中,将位置 2 和 6 的树木移走,剩下 [1, 3, 5] ,它们是美观的。

示例
输入:
[1,2,3,5,6]
2
输出:
2

解题:

  • 直接遍历,不满足的移除
class Solution {
public:
    /**
     * @param trees: the positions of trees.
     * @param d: the minimum beautiful interval.
     * @return: the minimum number of trees to remove to make trees beautiful.
     */
    int treePlanning(vector<int> &trees, int d) {
        // write your code here.
        int prev = trees[0], s = 0;
        for(int i = 1; i < trees.size(); ++i)
        {
            if(trees[i]-prev < d)//不满足
            {
                s++;//移除
            }
            else
            {
                prev = trees[i];
            }
        }
        return s;
    }
};

2. 正三角形拼接

题目链接

描述

给出 n 根木棍,每次切割可以将1根木棍切成 2 段。请计算出最少切割几次,可以从所有木棍中选出 3 根,组成一个三角形。

一开始的木棍根数为 n,3 ≤ n ≤ 1000。所有木棍的长度为一个整型数组 lengths,1 < length_i ≤ 10^9。
切割必须要将木棍分成 2 根整数长度的木棍,而且总长度要和原木棍相等。

说明

可以从长为7的木棍中,切出2根长为3的木棍,那么木棍的长度应该为[2,3,1,3,3,5],可以拼出边长为3的正三角形。

示例
输入:
[2375]
输出:
2

解题:

class Solution {
public:
    /**
     * @param lengths: the lengths of sticks at the beginning.
     * @return: return the minimum number of cuts.
     */
    int makeEquilateralTriangle(vector<int> &lengths) {
        // write your code here.
        map<int, int> m;
        int cut = 2;//最多切两次
        for(int i = 0; i < lengths.size(); i++)
            m[lengths[i]]++;//长度计数
        for(auto& mi : m)
        {
            if(mi.second == 2)//有2段了
            {
                if(m.lower_bound(mi.first+1) != m.end())
                    cut = min(cut, 1);//有更长的,切1次
            }
            else if(mi.second >= 3)//有3段了,不用切
                return 0;
            if(mi.first%2==0 && m.count(mi.first/2))
                cut = min(cut, 1);//如果能被平均切开,且有1段一样的,切1次就可以
        }
        return cut;
    }
};

3. 大楼间穿梭

题目链接

描述

蜘蛛侠在大楼间穿梭。大楼的高度可以看作是一个从左到右排列的数组。
现在蜘蛛侠站在第一栋大楼上,他想跳到最后一栋上。

  • 蜘蛛侠的视野为 k,他可以花费 x 点体力,用蛛丝移动到右侧k幢建筑中第一栋比当前位置高的大楼(应该是 高或者相同高度,题目有问题)。
  • 或者蜘蛛侠可以花费 y 点体力,跳跃到右侧接下来两栋大楼其中一栋。

请计算蜘蛛侠最少花费多少体力,到达最后一栋上。

大楼的高度为数组 heights,一共有 n 栋大楼,2 ≤ n ≤ 10^5,1 <= heights_i ≤ 10^9.蜘蛛侠的视野为 k,1 ≤ k ≤ n。两种行动的体力花费满足 1 ≤ x,y ≤ 10^9

说明
样例中,先花费6点体力跳到第三栋建筑,再花费10点到达最后一栋。

示例
输入:
heights=[154335]
k=3
x=10
y=6
输出:
16

解题:

  • 单调栈算出右侧高度 >= 当前的 第一个位置(逆序遍历)
  • 然后简单的动态规划即可
class Solution {
public:
    /**
     * @param heights: the heights of buildings.
     * @param k: the vision.
     * @param x: the energy to spend of the first action.
     * @param y: the energy to spend of the second action.
     * @return: the minimal energy to spend.
     */
    long long shuttleInBuildings(vector<int> &heights, int k, int x, int y) {
        // write your code here.
        int n = heights.size(), i, j, K;
        stack<int> s;
        vector<int> rightTall(n, -1);//右侧高或者相等的第一个位置
        for(int i = n-1; i >= 0; i--) //单调栈
        {
            while(!s.empty() && heights[s.top()] < heights[i])
            //右侧比我小的,没有用
                s.pop();
            if(!s.empty() && s.top()-i <= k)//有大于等于我的,且距离在 k 以内
                rightTall[i] = s.top();//记录下来
            s.push(i);
        }
        vector<long long> dp(n, LONG_LONG_MAX);
        dp[0] = 0;
        for(int i = 0; i < n; i++) // DP
        {
            if(dp[i] == LONG_LONG_MAX)
                continue;
            if(i+1 < n && dp[i+1] > dp[i]+y)//右侧1
                dp[i+1] = dp[i]+y;
            if(i+2 < n && dp[i+2] > dp[i]+y)//右侧2
                dp[i+2] = dp[i]+y;
            if(rightTall[i] != -1 && dp[rightTall[i]] > dp[i]+x)
                dp[rightTall[i]] = dp[i]+x;
                //直接跳到 k 以内的 第一个 >= 我的位置
        }
        return dp[n-1];
    }
};

4. 对称前后缀

题目链接

描述

给定一个字符串 s。我们令一个字符串的权值为一个字符串的最长对称前后缀长度。
请求出 s 的所有子串权值的总和
例如,”abcxyzcba” 的最长对称前后缀的长度为3,因为“abc”“cba”对称。
字符串的长度为 n,1 ≤ n ≤ 3*10^3。字符串均由小写英文字符组成。


说明

样例中,单个字符的子串的权值为1,它们的和为 7。
另外的权值为:
"bacb"->1
"bacbdab"->2
"bdab"->1
"acbda'->1




所以权值和为12。

示例
输入:
"bacbdab
输出:
12

解题:

  • 区间DP
class Solution {
public:
    /**
     * @param s: a string.
     * @return: return the values of all the intervals.
     */
    long long suffixQuery(string &s) {
        // write your code here
        long long n = s.size(), ans = n;
        //区间DP,dp[i][j] 表示区间内的 子串的最大权值
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for(int i = 0; i < n; i++)
            dp[i][i] = 1;
        for(int len = 1, i; len < n; len++)
        {
            for(i = 0; i+len < n; i++)
            {
                if(s[i] == s[i+len])//子串首尾相同
                {
                    int length = len-1;
                    //除去首尾 里面的子串[i+1, i+len-1]的长度
                    if(dp[i+1][i+len-1] == length)// 里面正反序都相同
                        dp[i][i+len] = dp[i+1][i+len-1]+2;
                        //把两个端点都可以加上
                    else
                        dp[i][i+len] = dp[i+1][i+len-1]+1;
                        //只能加上1端
                }
                ans += dp[i][i+len];
            }
        }
        return ans;
    }
};

我的CSDN博客地址 https://michael.blog.csdn.net/

长按或扫码关注我的公众号(Michael阿明),一起加油、一起学习进步!
阿里云 超级码力在线编程大赛初赛 第1场(第245名)-LMLPHP

08-31 19:02