1087 有多少不同的值 (20 分)

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]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值