1087 有多少不同的值 (20 分)
题意描述:
当自然数 n 依次取 1、2、3、……、N 时,算式 ⌊n/2⌋+⌊n/3⌋+⌊n/5⌋ 有多少个不同的值?(注:⌊x⌋ 为取整函数,表示不超过 x 的最大自然数,即 x 的整数部分。)
输入格式:
输入给出一个正整数 N(2≤N≤10^4 )。
输出格式:
在一行中输出题面中算式取到的不同值的个数。
输入样例:
2017
输出样例:
1480
解题思路:
Alice: 这道题看起来有点熟悉呀。
Bob: 题目做的多了,翻来覆去也就这么多类型了。
Alice: 这题是要去重啊,直接顺着题目的逻辑来就行了,遍历,求值,用set去重,最后输出集合的大小。
Bob: 嘿,咱俩想的一模一样啊。
Alice: 现在觉得那句“人生苦短,我用Python”还是有些道理的。Python中很多内置对象类型真的是使用频率很高的啊。比如说set( )
Bob: 是啊,C++的话还要再使用<set>
模板库。不过这道题目不用set
也能做,就是麻烦一点。
Alice: 不用set
⊙(・◇・)?用数组重复打标记,最后在把有标记的值找出来?
Bob: 对呀对呀,[]~ ( ̄▽ ̄)~*
代码:
- 一份Python实现,使用了
set( )
def main():
N = int(input())
# 接收输入的整数N
values = set()
# values是一个集合,用来存储所有的不同的值
for x in range(1, N + 1):
# 当自然数x依次取1 2 3 ... N时
values.add(int(x / 2) + int(x / 3) + int(x / 5))
# 将算式不同的值添加到values当中,由于集合中元素的唯一性,相同的值将只被记录一次
print(len(values))
# 由于集合中的每个元素都是唯一的,所以集合大小就是不同取值的个数
if __name__ == '__main__':
main()
- 一份C++语言实现,只使用了数组。
#include <stdio.h>
#define NN 10334
// 为什么是10334 ? 因为 10000 / 2 + 10000 / 3 + 10000 / 5 == 10333.3333...
int flags[NN];
// 我们使用flags来存储所有不同算式的值,如某个算式的值是0,就有flags[0] == 1
int main() {
int N, value;
scanf("%d", &N);
// 读入整数N
for(int i=1;i<=N; ++i){
// 对从1 到 N 的每个整数 i
value = int(i / 2) + int(i / 3) + int(i / 5);
// 计算这个整数对应的算式的值
//printf("%d %d %d\n", int(i/2), int(i/3), int(i/5));
flags[value] = 1;
// 记录这个值,如果有些值出现了多次,也只是重复执行,最终的记录结果和只出现一次是一样的。
}
int count = 0;
// 计算共有多少个不同的值出现
for(int i=0; i < NN; ++i){
if(flags[i] == 1){
// flags[i] == 1说明i作为某个算式的值至少出现了一次
count ++;
// i 是一个不同于其他值(..i-2,i-1, i+1,i+2...)的值
}
}
printf("%d\n", count);
// 输出共有多少个不同的值
return 0;
}
- 另一份C++语言实现,使用了
set( )
#include <stdio.h>
#include <set>
// 使用C++模板set需要先包含此文件
using namespace std;
// 同样需要声明命名空间
int main(){
int N;
set<int> values;
// 声明一个整型的集合容器
scanf("%d", &N);
// 读入正整数 N
for(int i=1; i<=N; ++i){
values.insert(int(i / 2) + int(i / 3) + int(i / 5));
// 向集合中添加 每个 n 对应的算式的值
}
printf("%d\n", values.size());
// 由于集合的特性,集合中没有重复的元素存在,所以集合的大小就是不同值的个数
return 0;
}
易错点:
- 使用数组实现的时候,
10000 / 2 + 10000 / 3 + 10000 / 5 == 10334
。对于边界值要多多关照,谨防数组越界访问带来的段错误。
总结:
-
这道题目中,我们遇到了一个比较常见的问题,去重。给定一个数组或者列表,其中有些元素重复出现,如何得到这些元素的集合呢 ?
如[1, 1, 2, 5,,5 ] => {1, 2, 5}
- 第一种方法: 使用编程语言的内置对象
set
,比如说Python。或者是语言的模板库,比如说C++语言的<set>
。向set( )
中直接传入一个待去重的列表就可以得到一个去重后的集合。或者先声明一个set
对象,然后一个一个的往里面添加元素,重复的元素将不会被再次添加。 - 第二种方法,使用数组或列表对不同的元素做出标记,然后找出所有不同的元素。如待去重数组是[
1, 1, 2, 5, 5]
, 其中最大元素是5
,,我们先声明一个 大小为6
的数组并初始化为[0, 0, 0, 0, 0, 0]
, 然后将对应的位置打上标记[0, 1, 1, 0, 0, 1]
, 最后得到去重后的元素数组[1, 2, 5]
。这种方法虽然看起来很酷,但是有一些缺点:1)复杂,2)需要以待去重元素中的最大值为大小的数组,占用内存较多。3 ) 鲁棒性差,无法处理多种数据类型的去重。如["U", 2, 2, 5, 5]
。
- 第一种方法: 使用编程语言的内置对象