本文已参与「新人创作礼」活动,一起开启掘金创作之路。
比赛链接:力扣 第 288 场周赛
题1——按奇偶性交换后的最大数字
题目链接:6037. 按奇偶性交换后的最大数字
难度:简单
题意: 给出一个数字,可以任意交换两个相同奇偶性相同的数字,返回最大的结果
1)比赛时的思路:
暴力枚举
1.用 List 把数字的每一位都存下来
2. 遍历 i ∈ [0, n) ,j ∈ [i, n),即从i开始,每次都将i后面比较大的数放前面
3. 最后根据List将结果拼起来
时间复杂度:O(N^2^)
class Solution {
public int largestInteger(int num) {
if(num <= 10) return num;
ArrayList<Integer>list = new ArrayList<>();
while(num > 0){
list.add(0, num % 10);
num /= 10;
}
int n = list.size();
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
if(i != j){
int a = list.get(i);
int b = list.get(j);
if(b > a && ((a % 2) == (b % 2))){
list.set(i, b);
list.set(j, a);
}
}
}
}
int ans = 0;
for(int i = 0; i < n; i++){
ans = ans * 10 + list.get(i);
}
return ans;
}
}
2)看完题解后的思路:
优先队列
使用两个奇偶降序优先队列,将数字分为奇偶两个部分
将int型变量通过String.valueOf()转为String,在通过String.toCharArray转为char数组,遍历该数组,根据数字的奇偶性选择队列入队。
入队后,在依次取出,使用StringBuilder拼接成String,在转成数字。
注意在处理时,不能改变所有元素的所处位置奇偶性,此时只需用boolean记录一下之前当前位置的奇偶性即可。
AC代码:
class Solution {
public int largestInteger(int num) {
// 奇数优先队列
Queue<Integer> odd = new PriorityQueue<>((a, b) -> (b - a));
// 偶数优先队列
Queue<Integer> even = new PriorityQueue<>((a, b) -> (b - a));
char[] ch = String.valueOf(num).toCharArray();
int n = ch.length;
boolean[] isOdd = new boolean[n];
// 入队
for(int i = 0; i < n; i++){
int a = ch[i] - '0';
if(a % 2 == 0){
even.offer(a);
}
else{
isOdd[i] = true;
odd.offer(a);
}
}
// 出队
StringBuilder s = new StringBuilder();
for(int i = 0; i < n; i++)
s.append(isOdd[i] ? odd.poll() : even.poll());
return Integer.parseInt(s.toString());
}
}
题2 —— 向表达式添加括号后的最小结果
题目链接:6038. 向表达式添加括号后的最小结果
难度:中等
题意:给出一字符串,形如a+b的形式,返回添加两个括号后,表达式的值为最小的结果字符串。
比如:12+34,则返回1(2+3)4,(乘号省略),此时结果最小。
思路: 1)比赛时的思路:
第一反应就是模拟,首先将字符串转化成字符数组,然后寻找左右括号的插入位置
后来发现可以使用之前复习到的"左右指针"的概念,即通过两个括号,将表达式分为四个部分,a*(b+c)*d
左指针从0开始,右指针从+加号开始。遍历每种情况,同时记录最优解(即最小值),如果比最优解还小,那么就计算最优解同时更新字符串。
最终没依然没AC,卡在了表达式计算上。
2)看完题解后的思路:
左右指针 + 模拟
同样是左右指针,将字符串分为三个部分即可,因为中间部分是直接相加的,一定存在。
左右部分是相乘的,可能不存在(比如左括号在最左边或者右括号在最右边)。
计算的思路就是分为三部分进行,最后就是枚举每一种可能
首先获取到➕的下标k,通过String类的indexOf方法则获取到下标。
设 、 分别是表示左右括号位置的左右指针。
枚举的范围是 ,
重点是每次截取三部分的范围,首先要知道String的substring方法是左闭右开的,即[a, b) ,所以substring(0, 0) 的结果是空
在枚举中使用 left,right表示当前左右括号插入的位置,那么则有:
左侧部分截取为[0, left-1] ,中间部分截取为[left, right-1],右边部分为[right, n-1]
AC代码:
class Solution {
public String minimizeResult(String exp) {
String ans = "";
int mini = Integer.MAX_VALUE;
int n = exp.length();
int k = exp.indexOf("+");
// 左右括号指针
for(int left = 0; left < k; left++){
for(int right = k + 1;right < n; right++){
// 往left插入左括号
String a = exp.substring(0, left);
// 往right插入右括号
String s = exp.substring(left, right+1);
// 计算往right插入右括号前,右括号的部分
String b = exp.substring(right+1);
// 计算表达式结果
int sum = cal(a, s, b);
// 更新最优解
if(sum < mini){
mini = sum;
ans = String.format("%s(%s)%s", a, s, b);
}
}
}
return ans;
}
public int cal(String a, String s, String b){
String[] split = s.split("\\+");
int res = toInt(split[0]) + toInt(split[1]);
if(!a.isEmpty()) res *= toInt(a);
if(!b.isEmpty()) res *= toInt(b);
return res;
}
public int toInt(String x){ return Integer.parseInt(x); }
}
题3——K 次增加后的最大乘积
题目链接: 6039. K 次增加后的最大乘积
难度: 简单
题意:给定一组数字,要求在限制的k次对某一位数字加1,使得最终每一位的乘积结果最大
1)比赛时思路:
比赛时思路比较乱,想着既然要操作k次,那么我该如何对这k次操作,每一次都往最大的数加1?这样的话每次都需要查找最大数
又或者先排序,然后分k个给每一个数字,多余的就重复继续分?
似乎都不太行,总之,思路很乱,没有想能用什么数据结构,也没办法模拟出来
2)看题解后的思路:
贪心 + 优先队列
看了题解豁然开朗,原来这是道贪心题,每次只需要增加最小的那个数就好。
当我们对一个数加1后会出现两种情况,① 这个数仍然是最小的数 ② 这个数已不是最小的数(说明之前有相同最小的数)
通过 升序的优先队列 可以很好的解决这个问题。
进行 k 次操作,每次操作都从优先队列取值,加一后继续入队。
最后将优先队列的元素相乘即为最终的结果:
AC代码:
class Solution {
public int maximumProduct(int[] nums, int k) {
Queue<Integer> q = new PriorityQueue<Integer> ();
for(int num : nums) q.offer(num);
for(int i = 0; i < k; i++)
q.offer(q.poll() + 1);
long ans = 1, MAX = 1000000007;
while(!q.isEmpty()){
ans = (ans * q.poll()) % MAX;
}
return (int) ans;
}
}
题4——花园的最大总美丽值
题目链接:6040. 花园的最大总美丽值
题目难度: 困难
题意: 给出一维数组,表示n个花园里种的花数,现剩下 x 朵花,可以选择任意花园种花,只能种不能采,实现一个种花策略能使得最后根据题目标准判定花园美丽值(分数)达到最大。返回这个最大值:
每个花园分为不完善和完善两种:若该花园种的花数目达到 target朵,则称为 完善花园。
花园总美丽值(即分数)的计算方法:
(完善花园数目 * 给定的full值 + 不完善花园中最少的花数目 * 给定的 partial值)
1)比赛时思路:
比赛时没有看这题,一般第四题都比较难,这题在比赛中的AC情况(203/6926),这里先写一点目前的思路吧...
读完这题我想到了这几天做到的股票问题,股票问题主要是购票和售票,在指定天数里,每天只能购票或者售票,最后求出最大的利润。
这一题则是种花,在原有的基础上种花,有n个花园,我可以选择其中1个花园种,也可以选择任意个,而且自己的花不用必须种完(想种就种)
目前的思路就是对题目进行了一点分析,看了会题解还是不太懂,放弃了,之后先把前三题掌握再说
总结
题1——按奇偶性交换后的最大数字(优先队列) 题2——向表达式添加括号后的最小值(左右指针 + 模拟) 题3——K次增加后的最大乘积(贪心 + 优先队列)(每次增加取最小的)
看完题解才发现,解题思路不难理解,就是比赛的时候没想出来,这里主要是对 优先队列、贪心使用不熟练,希望下次比赛再接再厉吧。