PAT A1049 Counting Ones

本文详细探讨了计算任意非负整数在二进制表示中1的数量的多种算法,包括直接位操作法、分治思想以及利用数学性质的递归公式。通过实例分析和代码实现,深入解析了不同方法的优缺点,适合对算法优化和二进制运算感兴趣的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PAT A1049 Counting Ones

在这里插入图片描述

Sample Input:

12

Sample Output:

5
  • 思路 1:当时写的什么玩意…/、
    从个位开始,遍历到最高位,计算每位可取1的个数,每位有三种情况,在这三种情况下,从0(now)xx~n这位能取到1的次数分别为:
    1)0:
    0(1)xxx、1(1)xxx…21(1)xxx…left-1(1)xxx(不能取到(left)(1)xxx超了)每种情况下有(位数*10)种情况(0(1)000 ~ 0(1)999、1(1)000 ~ 1(1)999…)
    2)1:
    和0相比,多了一种情况,即当右侧取到left时,多了:(left)(1)000~(left)(1)(right)==n,即多了right + 1
    3)>1:和0相比多了(left)(1)000~(left)(1)999,即多了(位数*10

  • TIPS:记得测试边界数据:1, 230 = 1073741824 = (int)pow(2.0, 30.0);

  • code 1:

#include <stdio.h>
#include <iostream>
using namespace std;
int main(){
	int n, cnt = 0;
	scanf("%d", &n);	
	int a = 1, ans = 0;
	while(n / a > 0){
		int right = n % a;
		int left = n / (a * 10);
		int now = n / a % 10;
		//如果当前位为0:从0—1的过程中,left可以使当前位为1的情况有01xxx-(left-1)1xxx = left种
		//每一种都有a(xxx 取 0 ~ (right-1) )个数 => ans += left*a 
		if(now == 0) ans += left * a;
		//当前位为1:使当前位为1的情况多了一种:即left取到left,这种情况不是平凡情况,后考虑
		//先不考虑这种情况,即求left0xxx,now=0:ans += left*a
		//当左侧取到left时:即变成求left1xxx的个数,为: right + 1(left1000 ~ left1right)(加的1 为left1000)
		else if(now == 1) ans += left * a + right + 1;
		//最后:当now>1时:left=0~left都能使now为1,且右侧全能取满a个数	
		else ans += (left + 1) * a;
		a *= 10;
	}
	printf("%d", ans);
	return 0;
}
  • T4 code:
#include <bits/stdc++.h>
using namespace std;

int main()
{
    char s[20];
    scanf("%s", s);
    int sum = 0;
    for(int i = 0; i < strlen(s); ++i)
    {
        char tmpl[20], tmpr[20];
        strncpy(tmpl, s, i); strcat(tmpl, "\0");
        strcpy(tmpr, s + i + 1);
        int L = atoi(tmpl);
        int R = atoi(tmpr);
        int numR = pow(10.0, strlen(s) - 1 - i);
        if(s[i] > '1')
        {
            sum += (L + 1) * numR;
        }else if(s[i] == '1')
        {
            sum += (L * numR + R + 1);
        }else
        {
            sum += L * numR;
        }
    }
    printf("%d", sum);
    return 0;
}

  • 思路2:
    [1, 10): 有1个1(1)
    [1, 100):有20个1 (1、11…91(10个)+ 10、11… 19(10个))
    [1,1000):有300个1 (普通:不考虑百位的1时,每1 ~ 99有20个1,即[1,100)的情况;特殊:考虑百位的1时多增了100个(100~199))

    当枚举到第i位时: [1, 10i ):P i = 10 x P i-1 (普通)+ 10 i-1 (特殊) (递推公式 1-1)
    如:
    i == 2时 [1, 100): P2 = 10 x P1 + 101 = 10 x 1 + 10 = 20;
    i == 3时 [1, 1000):P3 = 10 x P 2 + 10 2 = 10 x 20 + 100 = 300;

  • 求这个递推公式有什么用呢?
    任何一个数可以分为2部分:最高位 num[i] +其余低位 num[0 ~ i-1],

1. 若最高位比1大if(num[i] > 1):如,212 = 2 + 12 (1、2…199 + 200、201…212),包括:

① 高位产生的1的个数为(以212为例):1、2…199中,即[1,100)中1的个数 + [100,200)中1的个数

  • ①-1.不考虑最高位时:[1,100)中1的个数 = P 2 + [100,200)中1的个数 = P2, 即 num[i] * Pi
  • ①-2 考虑最高位时:[100,200)中最高位产生100个1,即,10i

② 低位含1个数(上一次递推已经求得):200、201…212中

2. 若最高位等于1if(num[i] == 1):如,112 = 1 + 12(1、2…99 + 100、101…112)

① [1,100)有:20个1,即,P2
② [100,112)有:

  • ②-1. 最高位1产生的13个1(12-0+1)
  • ②-2. 低位含1个数(上一次迭代)
3. 最高位为0:不产生新的1,保持上次迭代的结果
  • T2 code: 迭代实现
#include <bits/stdc++.h>
using namespace std;

int Count(int n){
	int p = 10, pi = 1, tmp = n / 10;
	int cnt = n % 10 > 0 ? 1 : 0;
	while(tmp){
		int dig = tmp % 10;
		if(dig > 1){
			cnt = cnt + p + dig * pi; 
		}else if(dig == 1){
			cnt = cnt + pi + (n % p + 1); 
		}
		tmp /= 10;
		pi = pi * 10 + p;
		p *= 10;
	}
	return cnt;
}
int main(){
	int n;
	scanf("%d", &n);
	printf("%d", Count(n));
	return 0;
} 
  • T2 code: 数组存储
#include <bits/stdc++.h>
using namespace std;
int Pi[15], P[15], num[15];

void Init(){
	P[0] = 1; Pi[0] = 0;
	for(int i = 1; i < 11; ++i){
		Pi[i] = 10 * Pi[i-1] + P[i-1]; //递推公式 1-1
		P[i] = 10 * P[i-1];
	}
	//Pi = 0, 1,  20,  300,  4000 ....
	//P  = 1, 10, 100, 1000, 10000 ....
}
int idex = 0;
void toArray(int n){
	while(n){
		num[idex++] = n % 10;
		n /= 10;
	}
}
int Count(int n){
	toArray(n);
	int cnt = num[0] ? 1 : 0;
	for(int i = 1; i < idex; ++i){
		if(num[i] > 1){
			cnt = num[i] * Pi[i] + P[i] + cnt; 	
			//    ①-1             ①-2   ②
		}else if(num[i] == 1){
			cnt = Pi[i] + (n % P[i] + 1) + cnt;
			//    ①        ②-1           ②-2
		}
	}
	return cnt;
}


int main(){
	int n;
	Init();
	scanf("%d", &n);
	printf("%d", Count(n));
	return 0;
} 

T3 code:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int para[15];
    char s[15];
    scanf("%s", s);
    para[0] = 0;
    for(int i = 1; i < 11; ++i)
    {
        para[i] = pow(10.0, i-1) + para[i-1] * 10;
    }
    int len = strlen(s), cnt = 0;
    for(int i = 0; i < len; ++i)
    {
        if(s[i] > '1')
        {
           cnt += (s[i] - '0') * para[len - 1 - i] + pow(10.0, (len - 1 - i));
        }else if(s[i] == '1')
        {
            char tmp[15];
            strcpy(tmp, s + i + 1);
            cnt += para[len - 1 - i] + atoi(tmp) + 1;
        }
    }
    printf("%d", cnt);
    return 0;
}
//0 1 20 300 4000 50000 600000 7000000 79999999 899999990 1410065307
基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
题目简述: 给定一个整数n,你需要求出1到n这n个整数的二进制(binary)表示中数字1出现的次数。例如,对于n=3,1到3这3个整数的二进制表示分别是:1, 10, 11,其中1出现的次数为2次。因此,输出2即可。 题目思路: 这道题可以用暴力求解法:枚举1到n的每个整数,然后统计对应的二进制表示中1的个数即可。但此种做法不适用于大数据。因此,我们需要寻找更为高效的算法。其中一种常见方法是用每一位的数字来计算。 例如,对于一个二进制数11010,我们可以从低到高,第一位统计在0~1范围内出现的次数,第二位统计在0~3范围内出现的次数,第三位在0~7范围内,第四位在0~15范围内,最后统计总体中的1的个数。因为,我们可以发现,对于n的第i位上的数字,它出现1的次数由(n / (i*10) * i)个1,除去i-1位之后,如果大于2,则还要加上i个1。再来看一个具体的例子,对于11010,第一位上有10种情况,0出现5次,1也出现5次,对于第二位,共有110, 101, 100, 011, 010, 001, 000七种情况,其中从00到01这两种情况中,每个数字都出现了两次1,因此一共出现了2*2=4次1。同样地,对于 101 和 011 这两种情况,也是如此。 因此,最终代码如下(基于C++): #include <iostream> using namespace std; #define int long long//避免溢出 signed main(){ int n, ans; cin >> n; for(int l = 1, r; l <= n; l = r + 1){ int k = n / l; r = n / k; ans += (r - l + 1) * (k / 10); int t = (k % 10) * l; if(t >= l) ans += t - l + 1; } cout << ans << endl; return 0; } 注意:本题的数据范围比较大,因此需要使用int long long类型避免溢出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值