给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。
nums 中的 K-or 是一个满足以下条件的非负整数:
只有在 nums 中,至少存在 k 个元素的第 i 位值为 1 ,那么 K-or 中的第 i 位的值才是 1 。
返回 nums 的 K-or 值。
注意 :对于整数 x ,如果 ( 2 i 2^i 2i AND x) == 2 i 2^i 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的数量。
代码:
class Solution {
public:
int findKOr(vector<int>& nums, int k) {
vector<int> n(32,0);
for(int& num : nums){
int i=0,t=num;
while(t){
n[i++]+=t%2;
t/=2;
}
}
long long ans=0, cnt=1;
for(int i=0;i<31;i++){
if(n[i]>=k) ans+=cnt;
cnt*=2;
}
return ans;
}
};
给你两个由正整数和 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
分析:
分别统计nums1和nums2的和以及0的数目。
据题意可得,只要二者都有0,一定能得到结果。
假设所有的0都变成1,与原来的和相加得到sum1和sum2:
代码:
class Solution {
public:
long long minSum(vector<int>& nums1, vector<int>& nums2) {
int n1,n2;
long long sum1,sum2;
n1=count(nums1.begin(),nums1.end(),0);
sum1=accumulate(nums1.begin(),nums1.end(),1LL*0)+n1;
n2=count(nums2.begin(),nums2.end(),0);
sum2=accumulate(nums2.begin(),nums2.end(),1LL*0)+n2;
long long t=sum1-sum2;
cout<<t<<endl;
if((t>0&&n2==0)||(t<0&&n1==0)) 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 。
提示:
分析:
如果nums[i] < k,只需要增量到k即可,如果nums[i]>=k 则不需要增量。
任何长度 大于或等于 3 的子数组,只需考虑长度为3的子数组,其余子数组一定包含了某一个长度为3的子数组。
设dp[i]:选择nums[i]进行增量,此前0 ~ i-1已经是美丽数组了,将nums[i]增量之后,0 ~ i 均为美丽数组。此时要保证每三个连续的数中有nums[j] >= k,前面的0 ~ i-1已经满足了美丽数组了,要使新增加的这个数增量之后,整个0 ~ i数组均为美丽数组有以下三种方式:
此时状态转移方程为:
d p [ i ] = m a x ( k − n u m s [ i ] , 0 ) + m i n ( d p [ i − 1 ] , m i n ( d p [ i − 2 ] , d p [ i − 3 ] ) ) dp[i] = max(k-nums[i], 0) + min(dp[i-1], min(dp[i-2], dp[i-3])) dp[i]=max(k−nums[i],0)+min(dp[i−1],min(dp[i−2],dp[i−3]))
最后,在完全计算所有的dp数组后,从dp[n-1], dp[n-2], dp[n-3] 中选取一个最小的值,因为只要保证最后三个中有一个是满足要求的就好了。
一些思考中的理解:
1、对于状态方程 d p [ i ] = m a x ( k − n u m s [ i ] , 0 ) + m i n ( d p [ i − 1 ] , m i n ( d p [ i − 2 ] , d p [ i − 3 ] ) ) dp[i] = max(k-nums[i], 0) + min(dp[i-1], min(dp[i-2], dp[i-3])) dp[i]=max(k−nums[i],0)+min(dp[i−1],min(dp[i−2],dp[i−3])),如果选择了dp[i] = max(k-nums[i], 0) + dp[j] (i-3<= j <= i-1),此时该方程代表的意思对nums[i] 和 nums[j]增量,i ~ j之间的都不增量了。
2、对于为什么要从i-1、i-2、i-3中选择一个再加上 max(k-nums[i], 0),明明选择了nums[i-2],此时就可以不选择nums[i]进行增量?
- 对于选择i-1或者i-2时,可以不选择i这一情况,在后面的dp[i+1]、dp[i+2]、dp[i+3]都会计算到,只要花销更小。
- 对于为什么选择了i-1或者i-2,还要考虑加上max(k-nums[i], 0)。因为是从dp[i-1]、dp[i-2]、dp[i-3]中选择一个更小的增量值,该转移方程一定会使0 ~ i的数组成为美丽数组,因此只需要考虑花销最小即可。
可以将nums中的每一个数字的范围辐射到最大,但是他的花销更大,尽管范围更大,但最优方案仍然不是他。
当然如果i是最后一个,则dp[i]、dp[i-1]、dp[i-2]就会互相比较,选择更小的,此时如果dp[i]是选择了dp[i-1]或dp[i-2],则dp[i]一定会>=min(dp[i-1], dp[i-2]),此时大概率不选择dp[i]。
代码:
记忆化搜索:
class Solution {
public:
long long dfs(vector<int>& nums,vector<long long>& pre, int i,int k){
if(i>=nums.size()) return 0;
if(pre[i]==-1){
long long cnt=max(k-nums[i],0) + dfs(nums, pre, i+1,k);
cnt=min(max(k-nums[i],0) + dfs(nums, pre, i+2,k), cnt);
cnt=min(max(k-nums[i],0) + dfs(nums, pre, i+3,k), cnt);
pre[i]=cnt;
}
return pre[i];
}
long long minIncrementOperations(vector<int>& nums, int k) {
vector<long long> pre(nums.size(),-1);
return min(dfs(nums,pre,0,k),min(dfs(nums,pre,1,k),dfs(nums,pre,2,k)));
}
};
动态规划,其实还可以降维:状态转移方程之和前面三个状态有关,只需要常数的空间
class Solution {
public:
long long minIncrementOperations(vector<int>& nums, int k) {
vector<long long> dp(nums.size(),0);
dp[0]=max(k-nums[0],0);dp[1]=max(k-nums[1],0);dp[2]=max(k-nums[2],0);
for(int i=3;i<nums.size();i++){
dp[i]=max(k-nums[i],0)+dp[i-3];
dp[i]=min(dp[i],max(k-nums[i],0)+min(dp[i-1],dp[i-2]));
}
return min(dp[nums.size()-1],min(dp[nums.size()-2],dp[nums.size()-3]));
}
};