第 412 场周赛

3264. K 次乘运算后的最终数组 I

给你一个整数数组 nums ,一个整数 k  和一个整数 multiplier 。

你需要对 nums 执行 k 次操作,每次操作中:

  • 找到 nums 中的 最小 值 x ,如果存在多个最小值,选择最 前面 的一个。
  • 将 x 替换为 x * multiplier 。

请你返回执行完 k 次乘运算之后,最终的 nums 数组。

示例 1:

输入:nums = [2,1,3,5,6], k = 5, multiplier = 2

输出:[8,4,6,5,6]

解释:

操作结果
1 次操作后[2, 2, 3, 5, 6]
2 次操作后[4, 2, 3, 5, 6]
3 次操作后[4, 4, 3, 5, 6]
4 次操作后[4, 4, 6, 5, 6]
5 次操作后[8, 4, 6, 5, 6]

示例 2:

输入:nums = [1,2], k = 3, multiplier = 4

输出:[16,8]

解释:

操作结果
1 次操作后[4, 2]
2 次操作后[4, 8]
3 次操作后[16, 8]

提示:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 100
  • 1 <= k <= 10
  • 1 <= multiplier <= 5

思路:由于数据规模小,直接暴力即可。

class Solution {
    public int[] getFinalState(int[] nums, int k, int multiplier) {
         for (int i = 0; i < k; i++) {
            // 找到最小值及其索引
            int minValue = Integer.MAX_VALUE;
            int minIndex = -1;
            for (int j = 0; j < nums.length; j++) {
                if (nums[j] < minValue) {
                    minValue = nums[j];
                    minIndex = j;
                }
            }
            // 将最小值替换为它乘以 multiplier 后的结果
            nums[minIndex] *= multiplier;
        }
        return nums;
    }
}

3265. 统计近似相等数对 I

给你一个正整数数组 nums 。

如果我们执行以下操作 至多一次 可以让两个整数 x 和 y 相等,那么我们称这个数对是 近似相等 的:

  • 选择 x 或者 y  之一,将这个数字中的两个数位交换。

请你返回 nums 中,下标 i 和 j 满足 i < j 且 nums[i] 和 nums[j] 近似相等 的数对数目。

注意 ,执行操作后一个整数可以有前导 0 。

示例 1:

输入:nums = [3,12,30,17,21]

输出:2

解释:

近似相等数对包括:

  • 3 和 30 。交换 30 中的数位 3 和 0 ,得到 3 。
  • 12 和 21 。交换12 中的数位 1 和 2 ,得到 21 。

示例 2:

输入:nums = [1,1,1,1,1]

输出:10

解释:

数组中的任意两个元素都是近似相等的。

示例 3:

输入:nums = [123,231]

输出:0

解释:

我们无法通过交换 123 或者 321 中的两个数位得到另一个数。

提示:

  • 2 <= nums.length <= 100
  • 1 <= nums[i] <= 10^6

思路:也是直接暴力。

class Solution {
    public int countPairs(int[] nums) {
        int result = 0;
        int n = nums.length;

        // 遍历每一对 (i, j) 且 i < j
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (areApproximateEqual(nums[i], nums[j])) {
                    result++;
                }
            }
        }

        return result;
    }
    private static boolean areApproximateEqual(int x, int y) {
        String strX = String.valueOf(x);
        String strY = String.valueOf(y);

        // 如果数字已经相等,直接返回true
        if (strX.equals(strY)) {
            return true;
        }

        // 尝试在strX中交换任意两个不同位置的字符,检查是否可以等于strY
        char[] charsX = strX.toCharArray();
        for (int p = 0; p < charsX.length; p++) {
            for (int q = p + 1; q < charsX.length; q++) {
                swap(charsX, p, q);
                String swappedStr = removeLeadingZeros(new String(charsX));
                if (swappedStr.equals(removeLeadingZeros(strY))) {
                    return true;
                }
                swap(charsX, p, q);  // 还原交换
            }
        }

        char[] charsY = strY.toCharArray();
        for (int p = 0; p < charsY.length; p++) {
            for (int q = p + 1; q < charsY.length; q++) {
                swap(charsY, p, q);
                String swappedStr = removeLeadingZeros(new String(charsY));
                if (swappedStr.equals(removeLeadingZeros(strX))) {
                    return true;
                }
                swap(charsY, p, q);  // 还原交换
            }
        }

        return false;
    }

    // 移除字符串中的前导0
    private static String removeLeadingZeros(String s) {
        int i = 0;
        while (i < s.length() && s.charAt(i) == '0') {
            i++;
        }
        return s.substring(i);
    }

    private static void swap(char[] chars, int i, int j) {
        char temp = chars[i];
        chars[i] = chars[j];
        chars[j] = temp;
    }
}

3266. K 次乘运算后的最终数组 II

给你一个整数数组 nums ,一个整数 k  和一个整数 multiplier 。

你需要对 nums 执行 k 次操作,每次操作中:

  • 找到 nums 中的 最小 值 x ,如果存在多个最小值,选择最 前面 的一个。
  • 将 x 替换为 x * multiplier 。

k 次操作以后,你需要将 nums 中每一个数值对 10^9 + 7 取余。

请你返回执行完 k 次乘运算以及取余运算之后,最终的 nums 数组。

示例 1:

输入:nums = [2,1,3,5,6], k = 5, multiplier = 2

输出:[8,4,6,5,6]

解释:

操作结果
1 次操作后[2, 2, 3, 5, 6]
2 次操作后[4, 2, 3, 5, 6]
3 次操作后[4, 4, 3, 5, 6]
4 次操作后[4, 4, 6, 5, 6]
5 次操作后[8, 4, 6, 5, 6]
取余操作后[8, 4, 6, 5, 6]

示例 2:

输入:nums = [100000,2000], k = 2, multiplier = 1000000

输出:[999999307,999999993]

解释:

操作结果
1 次操作后[100000, 2000000000]
2 次操作后[100000000000, 2000000000]
取余操作后[999999307, 999999993]

提示:

  • 1 <= nums.length <= 10^4
  • 1 <= nums[i] <= 10^9
  • 1 <= k <= 10^9
  • 1 <= multiplier <= 10^6

思路:1.当nums只有两个数的时候,如果我们操作到两个数接近也就是x<=y且x*m>y,那么后续操作一定是在x和y两个数之间交替进行的,

2.首先,直接用暴力模拟(用最小堆),直到原来的最大值,变成最小值,然后直接用公式计算出每个数还需要操作多少次。可以自己举例子模拟一遍就知道了。

即用:(最小堆+快速幂)

class Solution {
    private static final int MOD = 1_000_000_007;

    public int[] getFinalState(int[] nums, int k, int multiplier) {
        if (multiplier == 1) { // 数组不变
            return nums;
        }

        int n = nums.length;
        int mx = 0;
        PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> a[0] != b[0] ? Long.compare(a[0], b[0]) : Long.compare(a[1], b[1]));
        for (int i = 0; i < n; i++) {
            mx = Math.max(mx, nums[i]);
            pq.offer(new long[]{nums[i], i});
        }

        // 模拟,直到堆顶是 mx
        for (; k > 0 && pq.peek()[0] < mx; k--) {
            long[] p = pq.poll();
            p[0] *= multiplier;
            pq.offer(p);
        }

        // 剩余的操作可以直接用公式计算
        for (int i = 0; i < n; i++) {
            long[] p = pq.poll();
            nums[(int) p[1]] = (int) (p[0] % MOD * pow(multiplier, k / n + (i < k % n ? 1 : 0)) % MOD);
        }
        return nums;
    }
   
   
   //快速幂
    private long pow(long x, int n) {
        long res = 1;
        for (; n > 0; n /= 2) {
            if (n % 2 > 0) {
                res = res * x % MOD;
            }
            x = x * x % MOD;
        }
        return res;
    }
}

代码:

3267. 统计近似相等数对 II

注意:在这个问题中,操作次数增加为至多 两次 。

给你一个正整数数组 nums 。

如果我们执行以下操作 至多两次 可以让两个整数 x 和 y 相等,那么我们称这个数对是 近似相等 的:

  • 选择 x 或者 y  之一,将这个数字中的两个数位交换。

请你返回 nums 中,下标 i 和 j 满足 i < j 且 nums[i] 和 nums[j] 近似相等 的数对数目。

注意 ,执行操作后得到的整数可以有前导 0 。

示例 1:

输入:nums = [1023,2310,2130,213]

输出:4

解释:

近似相等数对包括:

  • 1023 和 2310 。交换 1023 中数位 1 和 2 ,然后交换数位 0 和 3 ,得到 2310 。
  • 1023 和 213 。交换 1023 中数位 1 和 0 ,然后交换数位 1 和 2 ,得到 0213 ,也就是 213 。
  • 2310 和 213 。交换 2310 中数位 2 和 0 ,然后交换数位 3 和 2 ,得到 0213 ,也就是 213 。
  • 2310 和 2130 。交换 2310 中数位 3 和 1 ,得到 2130 。

示例 2:

输入:nums = [1,10,100]

输出:3

解释:

近似相等数对包括:

  • 1 和 10 。交换 10 中数位 1 和 0 ,得到 01 ,也就是 1 。
  • 1 和 100 。交换 100 中数位 1 和从左往右的第二个 0 ,得到 001 ,也就是 1 。
  • 10 和 100 。交换 100 中数位 1 和从左往右的第一个 0 ,得到 010 ,也就是 10 。

提示:

  • 2 <= nums.length <= 5000
  • 1 <= nums[i] < 10^7

思路:用第二题的方法会超时,因此赛后找到别人的解法采用反向思维一下,我们遍历数组,将每个数的任意两个数字进行交换,查看多少能撞到一起的个数。

代码:

class Solution {
    public int countPairs(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
//计算每个数字出现的个数
        for (int n : nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        int res = 0;
        for (int n : nums) {
            int[] arr = trans(n);
            Set<Integer> set = new HashSet<>();
            map.put(n, map.get(n) - 1);
            for (int a = 0; a < 8; ++a) {
                for (int b = a + 1; b < 8; ++b) {
                    // 任意2位互换一次(第一次)
                    swap(arr, a, b);
                    {
                        int num = trans(arr);
                        if (!set.contains(num)) {
                            set.add(num);
                            res += map.getOrDefault(num, 0);
                        }
                    }
                    for (int c = 0; c < 8; ++c) {
                        for (int d = c + 1; d < 8; ++d) {
                            // 任意2位再互换一次(在第一次的基础上可以再次互换一次)
                            swap(arr, c, d);
                            {
                                int num = trans(arr);
                                if (!set.contains(num)) {
                                    set.add(num);
                                    res += map.getOrDefault(num, 0);
                                }
                            }
                            swap(arr, c, d);
                        }
                    }
                    swap(arr, a, b);
                }
            }
        }
        return res;
    }
    //转换为数组
    private int[] trans(int n) {
        int[] arr = new int[8];
        for (int i = 7; i >= 0; --i) {
            arr[i] = n % 10;
            n /= 10;
        }
        return arr;
    }
   //转化为整数
    private int trans(int[] n) {
        int res = 0;
        for (int i = 0; i < n.length; ++i) {
            res = res * 10 + n[i];
        }
        return res;
    }

    private void swap(int[] arr, int x, int y) {
        int tmp = arr[y];
        arr[y] = arr[x];
        arr[x] = tmp;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值