笔者为数不多AK的比赛
给你一个下标从 0 开始的整数数组 nums
和一个整数 k
。
请你用整数形式返回nums
中的特定元素之和 ,这些特定元素满足:其对应下标的二进制表示中恰存在k
个置位。
整数的二进制表示中的1就是这个整数的置位 。
例如,21
的二进制表示为10101
,其中有3
个置位。
1 <= nums.length <= 1000
1 <= nums[i] <= 10^5
0 <= k <= 10
显然是LowBit模版题,可以用LowBit求置位数,也可以直接用库函数求置位数,以下展示LowBit做法。
- //Java
- class Solution {
- public int sumIndicesWithKSetBits(List
nums, int k) { - int ans = 0;
- for(int i = 0;i
- int cnt = 0;
- int tmp = i;
- while(tmp>0){
- cnt+=1;
- tmp&=tmp-1;
- }
- if(cnt == k){
- ans+=nums.get(i);
- }
- }
- return ans;
- }
- }
- #Python
- class Solution:
- def sumIndicesWithKSetBits(self, nums: List[int], k: int) -> int:
- ans = 0
- for i,x in enumerate(nums):
- cnt = 0
- while i:
- cnt+=1
- i&=i-1
- if cnt == k:
- ans+=x
- return ans
时间&空间复杂度分析
- 时间复杂度:,n为数组长度,k为最大数的二进制位数。
- 空间复杂度:。
T2.让所有学生保持开心的分组方法数(Medium)
题目链接
题目描述
给你一个下标从 0 开始、长度为 n
的整数数组 nums
,其中 n
是班级中学生的总数。班主任希望能够在让所有学生保持开心的情况下选出一组学生:
如果能够满足下述两个条件之一,则认为第 i
位学生将会保持开心:
- 这位学生被选中,并且被选中的学生人数 严格大于
nums[i]
。 - 这位学生没有被选中,并且被选中的学生人数 严格小于
nums[i]
。
返回能够满足让所有学生保持开心的分组方法的数目。
输入范围
1 <= nums.length <= 10^5
0 <= nums[i] < nums.length
问题分析&思路
考虑如何选择k个学生,用贪心的思想考虑,被选中的k个学生的nums都要尽可能小,以便保证被选中的学生人数严格大于被选中的学生的nums;而没被选择的n-k个学生的nums都要尽可能大,以便保证被选中的学生人数严格小于没被选中的学生的nums。
因此,一个合理的想法就是先把数组升序排序(显然原数组的下标顺序对于本题无影响),对于排序好后的数组,对于每个i(0<=i如果nums[i],这就意味着可以选择前i+1个学生,同时后面n-i-1个未被选择的学生中的nums最小值是nums[i+1]>i+1个被选中的学生,可以保证这是一个合理的方案。
特别地,我们要特判一个都不选和全部都选这两种情况。
代码实现(Java&Python)
- //Java
- class Solution {
- public int countWays(List
nums) { - Collections.sort(nums);//数组排序
- int choose = 0;
- int ans = 0;
- int n = nums.size();
- if(nums.get(0)>0){//特判一个都不选
- ans+=1;
- }
- for(int i = 0;i
1;i++){ - if(nums.get(i)1&&i+1
1)){//判断是可行的分配 - ans+=1;
- }
- }
- if(nums.get(n-1)
//特判全都选 - ans+=1;
- }
- return ans;
- }
- }
- #Python
- class Solution:
- def countWays(self, nums: List[int]) -> int:
- nums.sort()
- ans = 0
- n = len(nums)
- if nums[0]>0:
- ans+= 1
- for i in range(0,n-1):
- if nums[i]1 and nums[i+1]>i+1:
- ans+=1
- if n>nums[-1]:
- ans+=1
- return ans
时间&空间复杂度分析
- 时间复杂度:,n为数组长度,时间复杂度卡在排序上面。
- 空间复杂度:。
T3.最大合金数(Medium)
题目链接
题目描述
假设你是一家合金制造公司的老板,你的公司使用多种金属来制造合金。现在共有 n
种不同类型的金属可以使用,并且你可以使用 k
台机器来制造合金。每台机器都需要特定数量的每种金属来创建合金。
对于第 i
台机器而言,创建合金需要 composition[i][j]
份 j
类型金属。最初,你拥有 stock[i]
份 i
类型金属,而每购入一份 i
类型金属需要花费 cost[i]
的金钱。
给你整数 n
、k
、budget
,下标从 1 开始的二维数组 composition
,两个下标从 1 开始的数组 stock
和 cost
,请你在预算不超过 budget
金钱的前提下,最大化 公司制造合金的数量。
所有合金都需要由同一台机器制造。
返回公司可以制造的最大合金数。
输入范围
1 <= n, k <= 100
0 <= budget <= 10^8
composition.length == k
composition[i].length == n
1 <= composition[i][j] <= 100
stock.length == cost.length == n
0 <= stock[i] <= 10^8
1 <= cost[i] <= 100
问题分析&思路
根据题意,所有合金都需要由同一台机器制造,显然制造的合金数越多,花费就越多,具有单调性,因此,可以想到用二分答案的方法二分去查找答案以降低时间复杂度,达到高效寻找答案的目的。
二分答案:即逆向验证,二分枚举可能的答案,然后对于答案进行判断,直至找到满足条件的答案。难点是判断函数的设计。
本题判断函数(check(lim))可以这样设计:对于所制造的合金数lim,判断是否存在机器能够制造lim个合金并且开销小于等于budget。
代码实现(Java&Python)
- //Java
- class Solution {
- public int maxNumberOfAlloys(int n, int k, int budget, List
> composition, List stock, List cost)
{ - long l = 0;
- long r = 200000000;//因为答案的最大值是budget+max(stock) <=2*10^8,所以设r为2*10^8
- while(l
//二分答案查找模版 - long mid = (l+r+1)>>1;
- if(check(mid,n,k,budget,composition,stock,cost)) l = mid;
- else r = mid-1;
- }
- return (int)l;
- }
- public boolean check(long lim,int n,int k,int budget,List
> composition,List stock, List cost)
{ - for(int i = 0;i
//遍历总共k台机器 - long c = 0;//计算每台机器的制作合金数目的开销
- List
lst = composition.get(i); - for(int j = 0;j
//遍历总共n种合金 - if(lst.get(j)*lim<=stock.get(j)) continue;
- c+=(lst.get(j)*lim - stock.get(j))*cost.get(j);//计算开销
- }
- if(c<=budget) return true;//如果这台机器的总开销低于预算则可行
- }
- return false;//没有机器的开销低于预算
- }
- }
- #Python
- class Solution:
- def maxNumberOfAlloys(self, n: int, k: int, budget: int, composition: List[List[int]], stock: List[int], cost: List[int]) -> int:
-
- def check(lim):
- for lst in composition:
- s = 0
- for i,x in enumerate(lst):
- if x*lim>stock[i]:
- s+=(x*lim-stock[i])*cost[i]
- if s<=budget:
- return True
- return False
-
- l,r = 0,10**9+1
- while l
- mid = (l+r+1)>>1
- if check(mid): l = mid
- else: r = mid - 1
- return l
时间&空间复杂度分析
- 时间复杂度:,其中k为机器数,n为合金总数,U = budget+max(stock)。
- 空间复杂度:。
T4.完全子集的最大元素和(Hard)
题目链接
题目描述
给你一个下标从 1 开始、由 n
个整数组成的数组。
如果一组数字中每对元素的乘积都是一个完全平方数,则称这组数字是一个 完全集 。
下标集 {1, 2, ..., n}
的子集可以表示为 {i1, i2, ..., ik}
,我们定义对应该子集的 元素和 为 nums[i1] + nums[i2] + ... + nums[ik]
。
返回下标集 {1, 2, ..., n}
的 完全子集 所能取到的 最大元素和 。
完全平方数是指可以表示为一个整数和其自身相乘的数。
输入范围
1 <= n == nums.length <= 10^4
1 <= nums[i] <= 10^9
问题分析&思路
由于所有的nums[i]都是正整数,显然是数越多,能取到的完全子集的元素和最大,所以以下完全集均考虑取在[1,n]范围内长度最大的完全集。
很容易想到的一个最大完全集是{1,4,9,16,……,k^2}(k^2<=n<(k+1)^2)。
试想一下,对于在某个完全集中已经出现的数,是否有可能出现在另一个完全集中?
答案显然是否定的,可以用反证法证明。假设x,y属于不同的完全集,而z同时属于x,y所属的完全集。则有x*z = a^2,y*z = b^2,那么有x*y = (a*b/z)^2,由于x,y都是正整数,显然a*b/z也是正整数,那么x,y相乘为完全平方数,是属于同一完全集的,因此假设不成立。自此,我们验证了各个完全集是独立的,即交集为空集。
在得到了上述结论之后,我们已经构造了一个完全集为{1,4,9,16,......},
那么我们考虑如何构造在[1,n]范围内的所有的完全集。显然{2*1,2*4,2*9,2*16,......}也是一个完全集,那么我们就很容易可以构造出[1,n]范围内的所有的最大完全集,即为k*{1,4,9,16,......}。最后我们输出所有完全集所对应下标的元素的最大和即可。
代码实现(Java&Python)
- //Java
- class Solution {
- public long maximumSum(List
nums) { - List
pf = new ArrayList<>();//1,4,9,16....完全集 - int n = nums.size();
- if(n == 1) return nums.get(0);//特判数组长度为1的情况
- long ans = 0;
- int i = 1;
- while(i<=n){//构造1,4,9,16....完全集
- pf.add(i*i);
- i+=1;
- }
- int[] vis = new int[n+1];//判断某一下标是否已经加入集合
- for(i = 1;i<=n;i++){
- if(vis[i] == 1) continue;
- int cnt = 0;
- long s = 0;//记录某一完全集的最大元素和
- while(i*pf.get(cnt)<=n){//当该完全集的最大元素超过n时退出循环
- s+=nums.get(i*pf.get(cnt)-1);
- vis[i*pf.get(cnt)] = 1;
- cnt+=1;
- }
- ans = Math.max(ans,s);//ans即取为最大值
- }
- return ans;
- }
- }
- #Python
- class Solution:
- def maximumSum(self, nums: List[int]) -> int:
- n = len(nums)
- if n == 1:return nums[0]
- fa = [i for i in range(n+1)]
- ans = 0
- vis = [0]*(n+1)
- pf = [i**2 for i in range(1,n+1)]
- for i in range(1,n+1):
- if vis[i]: continue
- tmp = 0
- l = 0
- while pf[l]*i<=n:
- tmp+=nums[pf[l]*i-1]
- vis[pf[l]*i] = 1
- l+=1
- ans = max(tmp,ans)
- return ans
时间&空间复杂度分析
- 时间复杂度:,循环次数可以近似看做n,因为每次遍历会确保把下标记录到vis数组中,因此每个下标仅访问操作一次,总共有n个下标,须访问n次。
- 空间复杂度:。
-
相关阅读:
火车卖票---Ticketer类
Invalid bound statement (not found)解决方法总结
网工内推 | 上市公司,云平台运维,IP认证优先,13薪
Redis-应用问题(缓存穿透/缓存击穿/缓存雪崩/分布式锁)
synchronized(string)
SQl 经验总结
Android笔记: 设置PopupMenu宽度
c++类对象的内存分布 以及 虚继承实现原理
VMware 安装Ubuntu22.04
【密码学】RSA密码体制存在的问题
-
原文地址:https://blog.csdn.net/m0_46121643/article/details/132944724