Halo,这里是Ppeua。平时主要更新C++,数据结构算法,Linux与ROS…感兴趣就关注我bua!
1.最大二进制奇数
🍉题目:
🍉例子:
🍉 题解:
首先看题目,最大二进制奇数,在一个二进制表示法当中,只要最后一位为1,这个数就是奇数,将一个字符串中原有的一重新排列组合,将1尽可能的放到高位.最后留一位放在低位即可.
假设给定字符串中1的数量为cnt.那么我们想要达到的就是如下关系
🍉代码解析:
具体思路如下:
-
遍历当前字符串,若为1则cnt++,并将当前位置置为0;
-
之后将低位也就是字符串的最后一位制成1,保证是奇数;
这里不需要考虑字符串没有1的情况,因为题给条件保证一定有一个1
-
从高位遍历,依次将当前为置为1,直到cnt为0则停止
class Solution {
public:
string maximumOddBinaryNumber(string s) {
int cnt=0;
for(auto &ss:s)
{
if(ss=='1')cnt++;
ss='0';
}
s[s.size()-1]='1';
cnt--;
for(int i=0;i<s.size()-1;i++)
{
if(cnt>0)
{
s[i]='1';
cnt--;
}
else break;
}
return s;
}
};
2. 2865. 美丽塔 I
🍉 题目:
🍉示例:
🍉题意:
先来分析下题目表达的意思:
给定一个maxHeights数组在其中选一个数为美丽塔的塔尖.塔的两边呈递减状态.
题给的maxHeights数组可以理解为是可以在这个地方建造美丽塔的最高高度,也就是塔高的上界.
当选择第i位maxHeights[i]为塔尖的时,则有[0][i-1]均小于maxHeights[i]、[i+1][n-1]均小于maxHeights[i].
对于[0]~[i-1]:则有s[i]<=s[i+1] (s为答案数组)
对于[i+1]~[n-1]:则有s[i+2]<=s[i+1] (s为答案数组)
具体关系如下图所示.
根据数据范围来推算法
我们需要学会一个方法: 根据数据范围来推导使用什么算法,c++中1s可以处理的次数为1e7,也就是超过这个时间就会报超时
具体的有如下关系,(数据来自acwing)
一般ACM或者笔试题的时间限制是1秒或2秒。
在这种情况下,C++代码中的操作次数控制在 1e7 为最佳。下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:
n≤30, 指数级别, dfs+剪枝,状态压缩dp
n≤100 => O(n3),floyd,dp
n≤1000 => O(n2),O(n2logn),dp,二分
n≤10000 => O(n∗√n),块状链表
n≤100000 => O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、dijkstra+heap、spfa、求凸包、求半平面交、二分
n≤1000000 => O(n), 以及常数较小的 O(nlogn) 算法 => hash、双指针扫描、kmp、AC自动机,常数比较小的 O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
n≤10000000 => O(n),双指针扫描、kmp、AC自动机、线性筛素数
n≤109 => O(√n),判断质数
n≤1018 => O(logn),最大公约数
在做题中有意识的先看数据范围,可以极大的帮助解题
那么我们先看这一题的数据范围
1 e 3 1e^3 1e3,也就是采用o( n 2 n^2 n2)的时间复杂度都可以通过.观察题目来看,我们直接暴力模拟两层循环即可.
注意,这里的数据大小范围为 1 e 9 1e^9 1e9,而int的范围大概为 2 e 9 2e^9 2e9左右.如果使用int来存储最终数据,很容易造成溢出,所以我们使用long long来存储
🍉 题解:
观察题目要求会发现,如果顺序遍历这个数组,很容易出现在出现一个极小的数字,从而影响了前面整个塔的建设.
也就是后续的高度会影响前面已经确定的结果,如果顺序遍历问题一下复杂起来了.所以我们选择从塔尖开始向前遍历.也就是逆序遍历.
整体思路如下:
设定一个值来存储已遍历区间的最小值(我们可以将这个最小值初始化为此时塔尖的高度:因为这半个区间中的所有高度都要小于等于塔尖)
若当前值小于等于这个最小值,则可以加入到答案中(因为我们是逆序遍历,此时说明他是非递增)
若当前值大于这个值,则将最小值加入到答案中.(maxHeights[cur]>=min,就会出现上图的情况)
左右两边进行一个对称的操作,之后将和加入到答案数组即可.(ans[i]表示以i位为塔尖此时的高度和)
最后对数组排序取出最后一个值,即为最大值.
class Solution {
public:
long long maximumSumOfHeights(vector<int>& maxHeights) {
//存储最大高度和
vector<long long>ans;
int n = maxHeights.size();
for (int i = 0; i < n; i++)
{
long long sum = maxHeights[i];
int min_heightl = maxHeights[i];
int min_heightr = maxHeights[i];
for (int j = i-1; j >=0; j--)
{
min_heightl = min(min_heightl, maxHeights[j]);
sum += min_heightl;
}
for (int j = i + 1; j < n; j++)
{
min_heightr = min(min_heightr, maxHeights[j]);
sum += min_heightr;
}
ans.push_back(sum);
}
sort(ans.begin(), ans.end());
return ans[n - 1];
}
};
3. 2866. 美丽塔 II
🍉题目
🍉示例:
🍉题意:
这题和美丽塔I的题目完全一样,唯一的区别就是
数据范围从 1 0 3 10^3 103,变成了 1 0 5 10^5 105这意味着不能使用O( n 2 n^2 n2)的算法了.我们需要真正的解决这道题.
🍉 题解:
这里我们可以用前后缀差分的方法,和我们之前美丽塔I的思想类似,也是从分别算出塔的两边再相加.但不同的是:我们仅遍历数组一遍来算出以当前位置为塔尖的(左边或者右边)的和
具体的我们利用单调栈来解决这道题:
以下以右边为例:
假设有数组[1,3,9,5,4,7]
我们第一个遇到的数字是7,此时把他加入到和中.
下一个遇到的数数字是4.因为是递减的排列,所以我们从右向左看.他很明显会被7给遮住.
所以我们将7踢出栈撤销之前更新的答案(也就是减去这个7),再将4入栈.并更新答案.
此时最关键的是,7是被压缩到4同样的高度,而不是被删掉了,也就是此时栈中有两个4.
那我们如何去记录这个栈中有x个y呢?
我们可以在栈中不存数字,存对应数字的数组下标.我们将栈中初始化一个数组大小n,此时只需要用前一个栈中下标减去要入栈的下标,就知道答案需要更新x个y了
右边栈初始值是n,左边栈初始值是-1.
注意用来存储当前和的数组需要用longlong 否则会爆
class Solution {
public:
long long maximumSumOfHeights(vector<int>& maxHeights) {
long long sum=0;
int n=maxHeights.size();
stack<int>right;
right.push(n);
vector<long long>rsum(n+1);
rsum.reserve(n+1);
//存储答案数组会爆int
vector<long long>lsum(n+1);
lsum.reserve(n+1);
//差分后缀
for (int i = n - 1; i >= 0; i--) {
while (right.size() > 1 && maxHeights[i] <= maxHeights[right.top()]) {
int value = right.top();
right.pop();
sum -= (long long)(right.top() - value) * maxHeights[value];
}
sum += (long long)(right.top() - i) * maxHeights[i];
right.push(i);
rsum[i] = sum;
}
long long ans=sum;
stack<int>left;
sum=0;
left.push(-1);
for(int i=0;i<n;i++)
{
while(left.size()>1&&maxHeights[i]<=maxHeights[left.top()])
{
int value=left.top();
left.pop();
sum-=(long long)(value-left.top())*maxHeights[value];
}
sum+=(long long)(i-left.top())*maxHeights[i];
ans=max(ans,sum+rsum[i+1]);
left.push(i);
lsum[i]=sum;
}
return ans;
}
};