给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。
nums 中的 K-or 是一个满足以下条件的非负整数:
只有在 nums 中,至少存在 k 个元素的第 i 位值为 1 ,那么 K-or 中的第 i 位的值才是 1 。
返回 nums 的 K-or 值。
注意 :对于整数 x ,如果 (2i AND x) == 2i ,则 x 中的第 i 位值为 1 ,其中 AND 为按位与运算符。
示例 1:
输入:nums = [7,12,9,8,9,15], k = 4
输出:9
解释:nums[0]、nums[2]、nums[4] 和nums[5] 的第 0 位的值为 1 。nums[0] 和 nums[5] 的第 1 位的值为 1 。 nums[0]、nums[1]和 nums[5] 的第 2 位的值为 1 。 nums[1]、nums[2]、nums[3]、nums[4] 和 nums[5] 的第 3位的值为 1 。 只有第 0 位和第 3 位满足数组中至少存在 k 个元素在对应位上的值为 1 。因此,答案为 2^0 + 2^3 = 9。
示例 2:
输入:nums = [2,12,1,11,4,5], k = 6
输出:0
解释:因为 k == 6 == nums.length ,所以数组的 6-or 等于其中所有元素按位与运算的结果。因此,答案为 2 AND 12 AND 1 AND 11 AND 4 AND 5= 0 。
示例 3:
输入:nums = [10,8,5,9,11,6,8], k = 1
输出:15
解释:因为 k == 1 ,数组的 1-or等于其中所有元素按位或运算的结果。因此,答案为 10 OR 8 OR 5 OR 9 OR 11 OR 6 OR 8 = 15 。
提示:
1 < = n u m s . l e n g t h < = 50 1 <= nums.length <= 50 1<=nums.length<=50
0 < = n u m s [ i ] < 2 31 0 <= nums[i] < 2^{31} 0<=nums[i]<231
1 < = k < = n u m s . l e n g t h 1 <= k <= nums.length 1<=k<=nums.length
分析:
按照题目意思模拟即可,将nums数组中的每一个数字的各个位数中,值为1的位置记录下来,用一个数组维护即可。
代码:
class Solution {
public:
int findKOr(vector<int>& nums, int k) {
int res = 0;
int n = nums.size();
vector<int>cnt(35);
for(int i = 0;i < n; i++){
int x = nums[i],j = 0;
while(x){
if(x & 1)cnt[j]++;
x >>= 1;
j++;
}
}
for(int i = 0; i <=31 ;i++){
if(cnt[i] >= k)res += pow(2, i);
}
return res;
}
};
给你两个由正整数和 0 组成的数组 nums1 和 nums2 。
你必须将两个数组中的 所有 0 替换为 严格 正整数,并且满足两个数组中所有元素的和 相等 。
返回 最小 相等和 ,如果无法使两数组相等,则返回 -1 。
示例 1:
输入:nums1 = [3,2,0,1,0], nums2 = [6,5,0]
输出:12
解释:可以按下述方式替换数组中的 0 :
- 用 2 和 4 替换 nums1 中的两个 0 。得到 nums1 = [3,2,2,1,4] 。
- 用 1 替换 nums2 中的一个 0 。得到 nums2 = [6,5,1] 。 两个数组的元素和相等,都等于 12 。可以证明这是可以获得的最小相等和。
示例 2:
输入:nums1 = [2,0,2,0], nums2 = [1,4]
输出:-1
解释:无法使两个数组的和相等。
提示:
1 < = n u m s 1. l e n g t h , n u m s 2. l e n g t h < = 1 0 5 1 <= nums1.length, nums2.length <= 10^5 1<=nums1.length,nums2.length<=105
0 < = n u m s 1 [ i ] , n u m s 2 [ i ] < = 1 0 6 0 <= nums1[i], nums2[i] <= 10^6 0<=nums1[i],nums2[i]<=106
分析:
这个题目按照题目意思去分类讨论即可,但在赛中的时候,我分类讨论的方法很复杂。其实因为要使得两个数组各自的和最小且相等,不难想到的是,可以先把所有的数字0用1替代,分别求和先得到 s u m 1 sum1 sum1和 s u m 2 sum2 sum2。
那么如何保证两个数组的和 s u m 1 sum1 sum1和 s u m 2 sum2 sum2相等呢?
代码:
赛时的代码,考虑了很多,仅供参考
class Solution {
public:
long long minSum(vector<int>& nums1, vector<int>& nums2) {
int zero1 = 0, zero2 = 0;
long long sum1 = 0, sum2 = 0;
for(auto x:nums1){
if(x == 0)zero1++;
sum1 += x;
}
for(auto x:nums2){
if(x == 0)zero2++;
sum2 += x;
}
long long res = 0;
if(sum1 > sum2){
long long esp = sum1 - sum2;
if(zero2 == 0)res = 0;
else if((long long)(esp + zero1) < zero2){
if(zero1 == 0)res = 0;
else res = sum2 + zero2;
}
else res = (long long)zero1 + esp + sum2;
}
else if(sum1 < sum2){
long long esp = sum2 - sum1;
if(zero1 == 0)res = 0;
else if((long long)(esp + zero2) < zero1 ){
if(zero2 == 0)res = 0;
else res = sum1 + zero1;
}
else res = (long long)zero2 + esp + sum1;
}
else{
if( (zero1 && (!zero2)) || (zero2 && (!zero1)) )res = 0;
else res = sum1 + max(zero1, zero2);
}
if(res == 0)res = -1;
return res;
}
};
赛后修改
class Solution {
public:
long long minSum(vector<int>& nums1, vector<int>& nums2) {
bool zero1 = 0, zero2 = 0;
long long sum1 = 0, sum2 = 0;
for(auto x:nums1){
if(x == 0)zero1 = true, ++sum1;
sum1 += x;
}
for(auto x:nums2){
if(x == 0)zero2 = true, ++sum2;
sum2 += x;
}
if( (sum1 > sum2 && zero1 && (!zero2)) || (sum2 > sum1 && zero2 && (!zero1)) || (sum1 != sum2 && (!zero1) && (!zero2)) )return -1;
return max(sum1, sum2);
}
};
给你一个下标从 0 开始、长度为 n 的整数数组 nums ,和一个整数 k 。
你可以执行下述 递增 运算 任意 次(可以是 0 次):
从范围 [0, n - 1] 中选择一个下标 i ,并将 nums[i] 的值加 1 。
如果数组中任何长度 大于或等于 3 的子数组,其 最大 元素都大于或等于 k ,则认为数组是一个 美丽数组 。
以整数形式返回使数组变为 美丽数组 需要执行的 最小 递增运算数。
子数组是数组中的一个连续 非空 元素序列。
示例 1:
输入:nums = [2,3,0,0,2], k = 4
输出:3
解释:可以执行下述递增运算,使 nums 变为美丽数组: 选择下标 i= 1 ,并且将 nums[1] 的值加 1 -> [2,4,0,0,2] 。 选择下标 i = 4 ,并且将 nums[4] 的值加 1 -> [2,4,0,0,3] 。 选择下标 i = 4 ,并且将 nums[4] 的值加 1 -> [2,4,0,0,4] 。 长度大于或等于 3 的子数组为 [2,4,0], [4,0,0], [0,0,4], [2,4,0,0], [4,0,0,4], [2,4,0,0,4] 。 在所有子数组中,最大元素都等于 k = 4 ,所以 nums 现在是美丽数组。 可以证明无法用少于 3次递增运算使 nums 变为美丽数组。 因此,答案为 3 。
示例 2:
输入:nums = [0,1,3,3], k = 5
输出:2
解释:可以执行下述递增运算,使 nums 变为美丽数组: 选择下标 i =2 ,并且将 nums[2] 的值加 1 -> [0,1,4,3] 。 选择下标 i = 2 ,并且将 nums[2] 的值加 1 ->[0,1,5,3] 。 长度大于或等于 3 的子数组为 [0,1,5]、[1,5,3]、[0,1,5,3] 。在所有子数组中,最大元素都等于 k = 5 ,所以 nums 现在是美丽数组。 可以证明无法用少于 2 次递增运算使 nums变为美丽数组。 因此,答案为 2 。
示例 3:
输入:nums = [1,1,2], k = 1
输出:0
解释:在这个示例中,只有一个长度大于或等于 3 的子数组 [1,1,2] 。其最大元素 2 已经大于 k = 1 ,所以无需执行任何增量运算。 因此,答案为 0 。
提示:
3 < = n = = n u m s . l e n g t h < = 1 0 5 3 <= n == nums.length <= 10^5 3<=n==nums.length<=105
0 < = n u m s [ i ] < = 1 0 9 0 <= nums[i] <= 10^9 0<=nums[i]<=109
0 < = k < = 1 0 9 0 <= k <= 10^9 0<=k<=109
分析:
如果数组中任何长度 大于或等于 3 的子数组,其 最大 元素都大于或等于 k ,则认为数组是一个 美丽数组 。其实意思就是只要任何一个长度为3的子数组,其中都有一个元素的值大于等于k即可。 因为任何一个长度大于3的子数组都会包含一个长度等于3的子数组。
那么我们如何去解答呢?
我们考虑第 i i i个元素是否要变成大于等于k的数,如果第 i i i个元素要增大到k,那么对其右边 i + 1 i+1 i+1的数字来说,其左边就有一个k了。
如果不增大,那么对其右边的数字来说,其左边就有一个没有达到k。
那么如果对 i + 2 i+2 i+2位置的数字来分析,如果 i + 1 i+1 i+1位置的数字也不去增大到k,那么其左边就有两个没有增大到k的数,那么它一定要增大(不去考虑右边的数,因为在后续讨论中,右边的数也会被涉及到的)。
那么这道题目可以使用dp的方法或者记忆化搜索来做。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示到第i个位置,离其最近的增大到k的数字的距离。
状态转移方程为:
d p [ i ] [ 0 ] = m i n ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) + m a x ( 0 , k − n u m s [ i ] ) ; dp[i][0] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]}) + max(0, k - nums[i]); dp[i][0]=min(dp[i−1][0],dp[i−1][1],dp[i−1][2])+max(0,k−nums[i]);
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] dp[i][1] = dp[i - 1][0] dp[i][1]=dp[i−1][0]
d p [ i ] [ 2 ] = d p [ i − 1 ] [ 1 ] dp[i][2] = dp[i - 1][1] dp[i][2]=dp[i−1][1]
d f s ( x , c n t ) dfs(x,cnt) dfs(x,cnt)表示到第x个位置时,前面有几个没有增到的k的数字。
记忆化搜索的方式为:
如果当前位置增大到k,则 d f s ( x + 1 , 0 ) + m a x ( 0 , k − n u m s [ i ] ) dfs(x + 1,0) + max(0, k - nums[i]) dfs(x+1,0)+max(0,k−nums[i])
如果 c n t < 2 cnt < 2 cnt<2,即前面有不超过2个没有增到k的数字,则当前位置可以不增加到k, d f s ( x + 1 , c n t + 1 ) dfs(x + 1,cnt + 1) dfs(x+1,cnt+1)
代码:
记忆化搜索
const int N = 1e5+5;
long long dp[N][3];
class Solution {
public:
//当前位置x是否要增大到k,且当前位置的后面有cnt个数字没有增大到了k
long long dfs(int x, int cnt, int k, vector<int>& nums){
if(x >= nums.size())return 0;
long long &res = dp[x][cnt];//引用类型 后续会更改
if(res != -1)return res;
res = dfs(x + 1, 0, k, nums) + max(0, k - nums[x]);//当前位置增大到k
if(cnt < 2)res = min(res, dfs(x + 1, cnt + 1, k ,nums));//如果前面有2个数字都没增大,则当前位置必须增大到k
return res;
}
long long minIncrementOperations(vector<int>& nums, int k) {
int n = nums.size();
memset(dp, -1, sizeof(dp));
return dfs(0, 0, k, nums);
}
};
class Solution:
def minIncrementOperations(self, nums: List[int], k: int) -> int:
n = len(nums)
dp = [[-1] * 3 for _ in range(n)]
def dfs(x, cnt):# 当前位置为x,cnt表示前面最近达到k的值离当前位置的距离
if x >= n:
return 0
if dp[x][cnt] != -1:
return dp[x][cnt]
dp[x][cnt] = dfs(x + 1, 0) + max(0, k - nums[x])
if cnt < 2:
dp[x][cnt] = min(dp[x][cnt], dfs(x + 1,cnt + 1))
return dp[x][cnt]
return dfs(0, 0)
dp
class Solution {
public:
long long minIncrementOperations(vector<int>& nums, int k) {
int n = nums.size();
vector<vector<long long>>dp(n, vector<long long>(3));
dp[0][0] = max(0, k - nums[0]);
for(int i = 1; i < n; i++){
dp[i][0] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]}) + max(0, k - nums[i]);
dp[i][1] = dp[i - 1][0];
dp[i][2] = dp[i - 1][1];
}
return min({dp[n - 1][0], dp[n - 1][1], dp[n - 1][2]});
}
};
class Solution:
def minIncrementOperations(self, nums: List[int], k: int) -> int:
n = len(nums)
dp = [[0] * 3 for _ in range(n)]
# dp[i][j] 表示当前数字i,没有到达k的数字离当前位置的距离为j
dp[0][0] = max(0, k - nums[0])
for i in range(1, n):
dp[i][0] = min(dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]) + max(0, k - nums[i])
dp[i][1] = dp[i - 1][0]
dp[i][2] = dp[i - 1][1]
return min(dp[n - 1][0], dp[n - 1][1], dp[n - 1][2])