xjj 的计原之旅

第一次出题,也顺便记录一下题目

x j j xjj xjj 的计原之旅

时间限制:1000ms

空间限制:128MB

那天, x j j xjj xjj 去上计原课,老师要求他求出 0 0 0 开始最少需要操作多少次能得到 n n n x j j xjj xjj 感觉到很头疼,他决定找万能的 y z y yzy yzy 帮忙,可是 y z y yzy yzy 觉得这题太简单了,做这题纯粹是浪费时间,于是 x j j xjj xjj 找到了你,希望你能够帮他完成这题。

每一次操作可以将当前数字加减乘除上一个2的任意非负幂次 ( 2 0 , 2 1 , 2 2 , 2 3 , 2 4 ⋯ ⋯ 2 ∞ ) (2^0,2^1,2^2,2^3,2^4\cdots\cdots2^∞) (20,21,22,23,242)

输入格式

输入一个 n ( 1 ≤ n ≤ 2 31 − 1 ) n(1 \leq n \leq 2^{31}-1) n(1n2311),表示目标数。

输出格式

输出一个正整数,表示最少的操作次数。

样例输入1

12

样例输出1

2

样例输入2

114514

样例输出2

6

样例1解释

第一次  0 + 16 = 16
第二次  16 - 4 = 12




思路1 时间复杂度 O ( l o g 2 n ) O(log_2n) O(log2n)

首先,对于一个数,从 0 0 0 得到 n n n ,与从 n n n 得到 0 0 0 无异。

每一次贪心选取大于等于 n n n 的第一个 2 x 2^x 2x ,和小于 n n n 2 x − 1 2^{x-1} 2x1

那么就有两个选择

  1. n = n − 2 x n = n - 2^x n=n2x 由于会变成负数,所以可以取负,变成 n = 2 x − n n = 2^x - n n=2xn
  2. n = n − 2 x − 1 n = n - 2^{x - 1} n=n2x1

因此只要每次贪心取这两个之中的最小值,求出让 n n n 变成 0 0 0 的步数

思路 1 1 1 s t d std std
#include<stdio.h>
int main() {
    int n, ans = 0;
    scanf("%d",&n);
    while(n) {
        long long x = 1;
        while(x < n) x *= 2;
        if(x - n < n - x / 2) n = x - n;
        else n = n - x / 2;
        ans++;
    }
    printf("%d",ans);
    return 0;
}



思路2 时间复杂度 O ( l o g 2 n ) O(log_2n) O(log2n)

根据性质

性质1     
	 100000000
	-000001000
	————————————
	 011111000
 
 
性质2    
	00000000 -> 00010000
	00010000 -> 00000000

因此就有两个操作

  1. 两步生成任意个数连续的 1 1 1
  2. 一步可以让任意位置的 01 01 01 互换 ( 0 (0 (0 变成 1 1 1 1 1 1 变成 0 ) 0) 0)

再考虑一种特殊情况

111101111 -> 111111111

在这种情况下就先一步将 0 0 0 转换成 1 1 1,再生成连续的一串 1 1 1

因此题解就是将 n n n 转换成二进制 01 01 01 串,判断生成每一段 1 1 1 最少需要几次

思路 2 2 2 s t d std std
#include<stdio.h>
int s[10005];
int main() {
    int n;
    scanf("%d",&n);
    int len = 0;
    s[len++] = 0;
    while(n) {
        if(n & 1) s[len++] = 1;
        else s[len++] = 0;
        n >>= 1;
    }
    s[len] = 0;
    int ans = 0;
    for(int i = 1;i < len;i++) {
        if(s[i - 1] == !s[i] && s[i + 1] == !s[i]) {
            s[i] = !s[i];
            ans++;
        }
    }
    int sum = 0;
    for(int i = 1;i <= len;i++) {
        if(s[i] == 1) sum++;
        else {
        	if(sum == 1) ans++;
        	else if(sum >= 2) ans += 2;
            sum = 0;
        }
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

he_69

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值