[Leetcode Weekly Contest]203
链接:LeetCode
[Leetcode]5495. 圆形赛道上经过次数最多的扇区
给你一个整数 n 和一个整数数组 rounds 。有一条圆形赛道由 n 个扇区组成,扇区编号从 1 到 n 。现将在这条赛道上举办一场马拉松比赛,该马拉松全程由 m 个阶段组成。其中,第 i 个阶段将会从扇区\(rounds[i - 1]\)开始,到扇区\(rounds[i]\)结束。举例来说,第 1 阶段从\(rounds[0]\)开始,到\(rounds[1]\)结束。
请你以数组形式返回经过次数最多的那几个扇区,按扇区编号 升序 排列。
实际上只需要关注第一个和最后一个扇区即可。 中间不管有多少个值多少圈, 对于每个扇区增加的次数都是相同的。
根据题意暴力即可。
class Solution {
public:
vector<int> mostVisited(int n, vector<int>& rounds) {
vector<int> res;
int m=rounds.size();
int start=rounds.front(),end=rounds.back();
if(start<=end){
for(int i=start;i<=end;++i){
res.push_back(i);
}
}
else{
for(int i=1;i<=end;++i){
res.push_back(i);
}
for(int i=start;i<=n;++i){
res.push_back(i);
}
}
return res;
}
};
[Leetcode]5496. 你可以获得的最大硬币数目
有 3n 堆数目不一的硬币,你和你的朋友们打算按以下方式分硬币:
每一轮中,你将会选出 任意 3 堆硬币(不一定连续)。
- Alice 将会取走硬币数量最多的那一堆。
- 你将会取走硬币数量第二多的那一堆。
- Bob 将会取走最后一堆。
重复这个过程,直到没有更多硬币。
给你一个整数数组 piles ,其中 piles[i] 是第 i 堆中硬币的数目。
返回你可以获得的最大硬币数目。
以三个为一组,你会取走硬币数量第二多的那一堆,贪心思想,在每次选取后只需要让剩余堆中的最大值最小。
class Solution {
public:
int maxCoins(vector<int>& piles) {
sort(piles.begin(),piles.end(),[](int l, int r) -> bool {
return l > r;});
int res=0,n=piles.size();
for(int i=0;i<n/3;++i){
res += piles[2*i+1];
}
return res;
}
};
[Leetcode]5497. 查找大小为 M 的最新分组
给你一个数组 arr ,该数组表示一个从 1 到 n 的数字排列。有一个长度为 n 的二进制字符串,该字符串上的所有位最初都设置为 0 。
在从 1 到 n 的每个步骤 i 中(假设二进制字符串和 arr 都是从 1 开始索引的情况下),二进制字符串上位于位置\(arr[i]\)的位将会设为 1 。
给你一个整数 m ,请你找出二进制字符串上存在长度为 m 的一组 1 的最后步骤。一组 1 是一个连续的、由 1 组成的子串,且左右两边不再有可以延伸的 1 。
返回存在长度 恰好 为 m 的 一组 1 的最后步骤。如果不存在这样的步骤,请返回 -1 。
设置两个哈希表,一个um代表每个位置上的1的长度,一个freq代表出现m的一组1的次数。在每次更新时,我们即可更新freq即可,为了消去由于合并可能会小时的一组1,我们在每次更新的时候,只需要更新连续1的最左端和最右端即可。
class Solution {
public:
int findLatestStep(vector<int>& arr, int m) {
int n=arr.size(),res=-1;
unordered_map<int,int> um;
unordered_map<int,int> freq;
for(int i=0;i<n;++i){
int cur=1,num=arr[i];
if(um.find(num-1)!=um.end()){
cur += um[num-1];
freq[um[num-1]] -= 1;
}
if(um.find(num+1)!=um.end()){
cur += um[num+1];
freq[um[num+1]] -= 1;
}
int left = um.find(num-1)!=um.end()?num-um[num-1]:num;
int right = um.find(num+1)!=um.end()?num+um[num+1]:num;
um[left] = cur;
um[right] = cur;
if(freq.find(cur)!=freq.end()){
freq[cur] ++;}
else{
freq[cur] = 1;
}
if((freq.find(m) != freq.end()&&(freq[m]>0))){
res = i+1;
}
}
return res;
}
};
[Leetcode]1563. 石子游戏 V
几块石子 排成一行 ,每块石子都有一个关联值,关联值为整数,由数组 stoneValue 给出。
游戏中的每一轮:Alice 会将这行石子分成两个 非空行(即,左侧行和右侧行);Bob 负责计算每一行的值,即此行中所有石子的值的总和。Bob 会丢弃值最大的行,Alice 的得分为剩下那行的值(每轮累加)。如果两行的值相等,Bob 让 Alice 决定丢弃哪一行。下一轮从剩下的那一行开始。
只 剩下一块石子 时,游戏结束。Alice 的分数最初为 0 。
返回 Alice 能够获得的最大分数 。
动态规划,比较容易能想到\(O(N^3)\)的做法。难点在于怎么实现\(O(N^2)\)的时间复杂度。在每一次DP的过程中,由于要比较取不同分割点时的最大值,额外消耗了O(N)的时间复杂度,正确的做法是通过考虑使用前缀和数组来优化时间,即,通过mx1,mx2两个数组存储每一次遍历过程中左端和右端的较大值,通过额外空间优化时间复杂度。
class Solution {
public:
int stoneGameV(vector<int>& st) {
int n = st.size(), sums[n + 1];
sums[0] = 0;
for(int i = 0; i < n; ++i)
sums[i + 1] = sums[i] + st[i];
int dp[n][n], mx1[n][n], mx2[n][n];
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; ++i)
mx1[i][i] = st[i], mx2[i][i] = st[i];
for(int sp = 2; sp <= n; ++sp) {
int m=0;
for(int i = 0, j = i + sp - 1; j < n; ++i, ++j) {
while(sums[m + 1] - sums[i] < sums[j + 1] - sums[m + 1])
++m;
if(m > i)
dp[i][j] = max(dp[i][j], mx1[i][m-1]);
if(m < j)
dp[i][j] = max(dp[i][j], mx2[m+1][j]);
if(sums[m + 1] - sums[i] == sums[j + 1] - sums[m + 1]) {
dp[i][j] = max(dp[i][j], mx1[i][m]);
}
mx1[i][j] = max(mx1[i][j-1], dp[i][j] + sums[j+1] - sums[i]);
mx2[i][j] = max(mx2[i+1][j], dp[i][j] + sums[j+1] - sums[i]);
}
}
return dp[0][n-1];
}
};