持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第36天,点击查看活动详情
题目链接:2310. 个位数字为 K 的整数之和
题目描述
给你两个整数 num 和 k ,考虑具有以下属性的正整数多重集:
每个整数个位数字都是 k 。
- 所有整数之和是
num。 - 返回该多重集的最小大小,如果不存在这样的多重集,返回
-1。
注意:
- 多重集与集合类似,但多重集可以包含多个同一整数,空多重集的和为
0。 - 个位数字 是数字最右边的数位。
提示:
示例 1:
输入:num = 58, k = 9
输出:2
解释:
多重集 [9,49] 满足题目条件,和为 58 且每个整数的个位数字是 9 。
另一个满足条件的多重集是 [19,39] 。
可以证明 2 是满足题目条件的多重集的最小长度。
示例 2:
输入: num = 37, k = 2
输出: -1
解释: 个位数字为 2 的整数无法相加得到 37 。
示例 3:
输入: num = 0, k = 7
输出: 0
解释: 空多重集的和为 0 。
整理题意
题目给定两个整数 num 和 k,求最少可以使用多少个位数字为 k 的整数凑成 num。
题目规定:
- 如果无法凑成
num返回-1; - 最少使用整数的个数为
0,也就是当num为0时,返回0; - 可以重复使用同一个整数。
解题思路分析
首先观察题目数据范围,num 在 3000 以内,数据范围较小,可以采用较为暴力的方式来解题。
观察题目特点,使用整数的个位数字都为 k,那么我们可以把 num 拆分为两个部分:num = n * k + 10 * m
n * k:表示总共使用n个数,且每个整数的个位数字都为k,那么总和的一部分就为n * k;10 * m:除开个位数字,剩下的就是十位数及以上的数字,所以可以表示为10 * m,这里的m为非负整数。也就是去掉n * k这部分后,剩下的数字一定是10的倍数,因为个位数已经确定。 根据num = n * k + 10 * m这个式子,因为已知num和k,我们需要求最小的n,那么可以通过从小到大枚举n来判断num - n * k是否为10的倍数,因为这里的m只需要为非负整数即可。
其次我们需要考虑 n 枚举的范围:
- 当
n为0时表示多重集和为空,根据题目规定此时返回0,此时对应num为0的情况,我们特判该情况即可。 - 考虑枚举
n的下界为1,因为至少需要一个整数凑成num; - 考虑枚举
n的上界为多少(难点),此时考虑最差的情况,k = 1的时候我们凑成num的最坏情况需要num个1,所以枚举上界为num(再观察num的数据范围再3000以内,确保不会超时)。
优化
我们可以发现当枚举的 n 超过 10 时,比如 n = 11 时,可以将 11 * k 继续拆分为 k + 10 * k,此时 10 * k 为 10 的倍数,可以将其放进 10 * m 中,从而 n 又回到了 1,此时可以使得选择的数仍然满足要求,并且 n 更小。
所以 n 枚举的上界可以优化为 10。
需要注意的是枚举上界包括
10。
具体实现
- 首先特判
num = 0的情况,返回0; - 在
[1, 10]中从小到大枚举答案n,返回最小的能够使得(num - n * k)为10的倍数的n。
需要注意
num - n * k需要为非负整数,也就是num - n * k >= 0
- 如果在
[1, 10]中无法找到满足条件的n,也就是不存在这样的多重集,此时返回-1。
复杂度分析
- 时间复杂度:,最多遍历
10次,仅需常数时间。 - 空间复杂度:,仅需常数存储空间。
代码实现
class Solution {
public:
int minimumNumbers(int num, int k) {
//特判num为0时空多重集
if(num == 0) return 0;
//[1, 10]枚举答案
for(int i = 1; i <= 10 && i * k <= num; i++){
if((num - i * k) % 10 == 0) return i;
}
//不存在这样的多重集
return -1;
}
};
总结
- 该题通过观察其特点,分析出组成
num的两个部分num = n * k + 10 * m是解题关键。 - 其次考虑使用 枚举 的方法来搜索答案,同时需要考虑枚举的上下界,以及边界情况是否需要特判。
- 最后考虑枚举范围是否可以优化。
- 测试结果:
结束语
人生有自己定义,不必为迎合别人而焦虑;人生无法重来,不必因害怕失去而徘徊。心态好的人,不是不会遇到困难,而是懂得把发生的事情当成垫脚石,不断向下扎根、向上成长。愿你用好的心态,过出独一无二的生活。新的一天,加油!