太久没打算法竞赛了,代码写的很糙,赛时做出前3题,本文记录下3题赛时代码+最后一题的补题代码和思路
1. 数组中的最大数对和
题目:给你一个 非空 链表的头节点 head ,表示一个不含前导零的非负数整数。将链表 翻倍 后,返回头节点 head 。
输入:head = [1,8,9]
输出:[3,7,8]
解释:上图中给出的链表,表示数字 189 。返回的链表表示数字 189 * 2 = 378 。
class Solution {
public ListNode doubleIt(ListNode head) {
ListNode newHead = reverse(head);
ListNode ans = new ListNode();
ListNode cur = ans;
int t = 0;
while(newHead != null) {
int val = newHead.val * 2;
val += t;
if(val >= 10){
val = val % 10;
t = 1;
}
else t = 0;
cur.next = new ListNode(val);
// System.out.println(val);
cur = cur.next;
newHead = newHead.next;
}
if(t != 0) cur.next = new ListNode(1);
return reverse(ans.next);
}
ListNode reverse(ListNode head){
ListNode now = head;
ListNode newHead = null;
while(now != null) {
ListNode t = now.next;
now.next = newHead;
newHead = now;
now = t;
}
return newHead;
}
}
2. 翻倍以链表形式表示的数字
题目:给你一个下标从 0 开始的整数数组 nums 。请你从 nums 中找出和 最大 的一对数,且这两个数数位上最大的数字相等。
返回最大和,如果不存在满足题意的数字对,返回 -1
输入:nums = [51,71,17,24,42]
输出:88。
解释:
i = 1 和 j = 2 ,nums[i] 和 nums[j] 数位上最大的数字相等,且这一对的总和 71 + 17 = 88 。
i = 3 和 j = 4 ,nums[i] 和 nums[j] 数位上最大的数字相等,且这一对的总和 24 + 42 = 66 。
可以证明不存在其他数对满足数位上最大的数字相等,所以答案是 88 。
class Solution {
public int maxSum(int[] nums) {
int ans = -1;
for(int i = 0; i < nums.length;i ++ ) {
for(int j = i + 1 ; j < nums.length; j ++ ) {
int t = nums[i] + nums[j];
if(t > ans && judge(nums[i],nums[j])){
ans = t;
}
}
}
return ans;
}
boolean judge(int x, int y) {
int t1 = 0, t2 = 0;
while(x > 0){
int t = x % 10;
if(t > t1) t1 = t;
x /= 10;
}
while(y > 0){
int t = y % 10;
if(t > t2) t2 = t;
y /= 10;
}
return t1 == t2;
}
}
3. 限制条件下元素之间的最小绝对差
题目:给你一个下标从 0 开始的整数数组 nums 和一个整数 x 。
请你找到数组中下标距离至少为 x 的两个元素的 差值绝对值 的 最小值 。
换言之,请你找到两个下标 i 和 j ,满足 abs(i - j) >= x 且 abs(nums[i] - nums[j]) 的值最小。
请你返回一个整数,表示下标距离至少为 x 的两个元素之间的差值绝对值的 最小值 。
输入:nums = [4,3,2,4], x = 2
输出:0
解释:我们选择 nums[0] = 4 和 nums[3] = 4 。
它们下标距离满足至少为 2 ,差值绝对值为最小值 0 。
0 是最优解。
class Solution {
public int minAbsoluteDifference(List<Integer> nums, int x) {
int n = nums.size();
TreeSet<Integer> set = new TreeSet<>();
int ans = Integer.MAX_VALUE;
for(int i = 0; i < n; i ++) {
if(i >= x) {
int e = nums.get(i - x);
set.add(e);
}
Integer a = set.floor(nums.get(i));
Integer b = set.ceiling(nums.get(i));
if(a != null) ans = Math.min((Integer)Math.abs(a - nums.get(i)), ans);
if(b != null) ans = Math.min((Integer)Math.abs(b - nums.get(i)), ans);
}
return ans;
}
}
4. 操作使得分最大
题目:给你一个长度为 n 的正整数数组 nums 和一个整数 k 。
一开始,你的分数为 1 。你可以进行以下操作至多 k 次,目标是使你的分数最大:
- 选择一个之前没有选过的 非空 子数组 nums[l, …, r] 。
- 从 nums[l, …, r] 里面选择一个 质数分数 最高的元素 x 。如果多个元素质数分数相同且最高,选择下标最小的一个。
- 将你的分数乘以 x 。
`nums[l, …, r] 表示 nums 中起始下标为 l ,结束下标为 r 的子数组,两个端点都包含。
一个整数的 质数分数 等于 x 不同质因子的数目。比方说, 300 的质数分数为 3 ,因为 300 = 2 * 2 * 3 * 5 * 5 。
请你返回进行至多 k 次操作后,可以得到的 最大分数 。
由于答案可能很大,请你将结果对 109 + 7 取余后返回。
输入:nums = [8,3,9,3,8], k = 2
输出:81
解释:进行以下操作可以得到分数 81 :
- 选择子数组 nums[2, …, 2] 。nums[2] 是子数组中唯一的元素。所以我们将分数乘以 nums[2] ,分数变为 1 * 9 = 9 。
- 选择子数组 nums[2, …, 3] 。nums[2] 和 nums[3] 质数分数都为 1 ,但是 nums[2] 下标更小。所以我们将分数乘以 nums[2] ,分数变为 9 * 9 = 81 。
81 是可以得到的最高得分。
思路:
- 先求出数组元素的质数分数
- 再用单调栈向前扫和向后扫求出这个质数分数能贡献左边的元素个数和右边的元素个数
- 左边的元素个数和右边的元素个数相乘就是这个质数分数所能贡献的区间数
- 将数组元素按照质数分数的值从大到小遍历,用快速幂计算:
质数分数
的能贡献的区间数
次方, 然后再与之前计算的 分数 向乘,操作数 k 每次减去这个质数分数贡献的区间数,直到 k 减为 0
代码:
class Solution {
static int N = 10_0000;
static int[] p = new int[N + 1];
static void init(){
boolean[] used = new boolean[N + 1];
for(int i = 2; i <= N; i ++ ) {
if(!used[i]){ //是质数
for(int j = i; j <= N; j += i) {
p[j] ++;
used[j] = true;
}
}
}
}
public int maximumScore(List<Integer> nums, int k) {
init();
int n = nums.size();
int[] left = cal(nums, true);
int[] right = cal(nums, false);
Integer[] idxs = new Integer[n];
for(int i = 0; i < n; i ++) idxs[i] = i;
Arrays.sort(idxs, (a, b) -> nums.get(b) - nums.get(a));
long ans = 1;
long mod = 10_0000_0007l;
for(int i = 0; i < n; i ++ ) {
int idx = idxs[i];
int num = left[idx] * right[idx];
// System.out.println(left[idx] + " " + right[idx]);
int val = nums.get(idx);
// System.out.println(num);
int dk = Math.min(num, k);
ans = ans * qpow(val, dk, mod) % mod;
k-= dk;
}
return (int)ans;
}
int[] cal(List<Integer> nums, boolean flag) {
int n = nums.size();
int[] res = new int[n];
if(flag) {
Deque<int[]> deque = new ArrayDeque<>();
deque.offer(new int[]{Integer.MAX_VALUE, -1});
for(int i = 0; i < n; i ++ ) {
int e = nums.get(i);
int val = p[e];
while(!deque.isEmpty() && deque.peekLast()[0] < val){
deque.pollLast();
}
res[i] = i - deque.peekLast()[1];
deque.offerLast(new int[]{val, i});
}
}
else {// 4 2 3 1 5 1 2 3 5 4
Deque<int[]> deque = new ArrayDeque<>();
deque.offerLast(new int[]{Integer.MAX_VALUE, n});
for(int i = n - 1; i >= 0; i -- ) {
int e = nums.get(i);
int val = p[e];
while(!deque.isEmpty() && deque.peekLast()[0] <= val){
deque.pollLast();
}
res[i] = deque.peekLast()[1] - i;
deque.offerLast(new int[]{val, i});
}
}
return res;
}
// 快速幂
long qpow(long base, int v, long mod) {
long res = 1l;
base %= mod;
while (v > 0) {
if ((v & 1) == 1) {
res = res * base % mod;
}
base = base * base % mod;
v = v >> 1;
}
return res;
}
}