给你一个整数数组 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;
}
}
给你一个正整数数组 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;
}
}
给你一个整数数组 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;
}
}
代码:
注意:在这个问题中,操作次数增加为至多 两次 。
给你一个正整数数组 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;
}
}