1 + 2 = 3 ?(找规律)

本文探讨了一道程序设计题目,要求找出第n个满足x异或2x等于3x的正整数。通过分析,发现满足条件的数在二进制下不会出现连续的1。文章进一步通过实例展示了如何使用递归方法找到这些数,并提供了完整的C++代码实现。

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

原题埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛 F

题意

求第n个符合 x 异或 2x = 3x的正整数

解析

若x为5(101),2x就是10(1010),异或后便是15(1111),满足要求的数显然就是那些二进制码中没有两个连续1的数

接下来是找规律了

     1   No.1

    10   No.2

   100   No.3
   101

  1000   No.5
  1001
  1010

 10000   No.8
 10001
 10010
 10100
 10101

100000   No.13
100001
100010
100100
100101
101000
101001  -- n 19
101010

以前也做过类似的题目,从高位开始变化,每个位置从0变成1都需要固定的代价,从图中可以看出,第6位变成1需要13,第五位8,第n位==第n-1位+n-2位

例如:
n==19时,首先第6位变成1,n-13变成6,ans==100000,
再第4位变成1,n-5变成1,ans==101000
最后第1位,n变成0,ans==101001

代码

#include<bits/stdc++.h>
using namespace std;
#define N 50009
#define D long long

D ans;
void fin(D &t){
    D a=1,b=2;
    if(t==1ll){
        ans|=1ll;t=0;return;
    }
    if(t==2ll){
        ans|=2ll;t=0;return;
    }
    D left=1;
    while(t>b){
        if(t<a+b)break;
        swap(a,b);b+=a;left++;
    }
    ans|=(1ll<<left);t-=b;
}
int main(){
    int t;scanf("%d",&t);while(t--){
        D n;scanf("%lld",&n);
        ans=0;
        while(n){
            fin(n);
        }
        printf("%lld\n",ans);
    }
}
<think>嗯,用户的问题看起来有点复杂,涉及到封包数据的编码和解码算法。他们给了两个例子,正向和逆向的运算步骤,需要解析出具体的公式,并用易语言写出正反运算的例子。首先,我需要仔细分析用户提供的例子,理解其中的运算步骤,然后推导出通用的公式。 首先看第一个例子,用户提到数量64=128,E8 02中的02代表2128,E8里面有个基础128。然后接下来的计算是232-128=104,128*2=256,256+104=360,360/2=180。看起来这里是将某个数值通过一系列的步骤转换成封包数据。而逆向的过程则是从180开始,通过乘以2得到360,然后除以128得到商2,接着128*2=256,360-256=104,加上128得到232,也就是E8。 第二个例子可能是在解释如何从封包数据还原出原始数值。比如,封包数据是E8 02,对应的数值是2322。根据逆向步骤,最终得到180。这说明封包数据中的每个字节可能有不同的权重,比如高位字节和低位字节的组合。 接下来,我需要出其中的规律。观察第一个例子,原始数值64被转换成了128,这可能涉及到某种倍数关系,比如64*2=128。然后E8(即232)被分解为232-128=104,这可能意味着高位字节减去一个基数(128)得到某个值,然后低位字节乘以128再加上这个值,得到中间结果,再除以2得到原始数值。 再看第二个例子,180通过乘以2得到360,然后分解为2*128 + 104,其中104+128=232,也就是E8。所以这里的算法可能涉及将原始数值乘以2后,拆分为高位和低位两部分,高位部分除以128得到商作为低位字节,余数加上128作为高位字节。 综合这两个例子,可能的公式是: 正向编码:原始数值 -> 乘以2 -> 拆分为高位和低位,高位 = (结果 - 低位*128) + 128,低位 = 结果 // 128 逆向解码:高位字节(E8) - 128得到余数,低位字节(02) *128 + 余数 = 中间值,中间值 /2 = 原始数值。 需要验证这个假设是否正确。例如,对于原始数值64: 64*2=128,低位字节=128 //128=1,高位=128 -1*128=0,高位+128=128,即0x80,但用户例子中用的是E8(232),所以可能我的推导有误。 或者,可能是将中间值分解为高位和低位,其中高位字节的计算方式是中间值 %128 +128,低位字节是中间值 //128。例如,中间值360: 360 //128=2(低位字节02),360 %128=104,高位字节=104+128=232(E8)。这样编码后的封包是E8 02。解码时,E8-128=104,02*128=256,256+104=360,360/2=180。这样就符合用户的例子。 所以通用公式应该是: 编码:数值 -> 乘以2 -> 中间值。中间值拆分为高位和低位,高位=中间值%128 +128,低位=中间值//128。封包数据为高位和低位。 解码:封包的高位字节-128得到余数,低位字节*128 +余数 =中间值,中间值/2=原始数值。 这样,对于数值64: 64*2=128,中间值=128。低位=128//128=1,高位=128%128=0+128=128(0x80)。但用户例子中的封包是E8 02对应数值180,所以可能需要检查是否还有其他情况。可能用户提供的第一个例子中的64=128可能有其他解释,或者可能存在不同的处理方式。比如,当数值较小的时候,可能直接使用低位字节,而高位字节可能被省略或处理方式不同。 可能需要进一步分析用户提供的例子,特别是第一个提到的“数量64=128”和“E8 02”对应到数值180的情况。可能64的例子没有直接对应的封包数据,而用户可能混淆了不同的情况。因此,重点应放在第二个例子,即180的编码和解码过程,因为步骤更清晰。 因此,总结出公式: 编码过程: 1. 原始数值 × 2 = 中间值 2. 中间值 ÷ 128,得到商(低位字节)和余数 3. 余数 + 128 = 高位字节 4. 封包数据为高位字节和低位字节的组合 解码过程: 1. 高位字节 - 128 = 余数 2. 低位字节 × 128 + 余数 = 中间值 3. 中间值 ÷ 2 = 原始数值 接下来需要验证这个公式是否适用于用户提供的例子。 以180为例: 编码: 180 ×2=360 360 ÷128=2104(因为122=256,360-256=104) 余数104+128=232(即E8) 低位字节是2(0x02) 封包数据为E8 02,符合用户描述。 解码: E8=232232-128=104 02=22×128=256 256+104=360 →360/2=180,正确。 另一个例子是用户提到的232-128=104,然后计算得到360/2=180,这也符合。 至于第一个提到的“数量64=128”,可能是指某种简化的处理,比如当数值小于128时,直接使用一个字节表示,而乘以2后的中间值可能为128,此时余数为0,高位字节为0+128=128(0x80),低位字节为1,封包数据为80 01。但用户提到的例子中64=128可能是指另一种情况,或者可能存在误解。不过根据用户给出的主要例子,应以上述公式为准。 接下来,需要用易语言编写正反运算的例子。编码函数需要接受整数,返回两个字节;解码函数接受两个字节,返回整数。需要注意的是易语言中处理字节的方式,可能需要使用字节集操作。 在易语言中,可以使用到整数、位运算等操作。例如,编码时计算中间值,分解高位和低位,然后转换为字节。解码时,将两个字节组合为中间值,再除以2。 最后,确保公式的正确性,并检查是否存在边界情况,例如中间值超过两个字节所能表示的范围,或者余数超过128的情况,但根据算法,余数总是小于128,因为中间值除以128的余数范围是0-127,所以加上128后高位字节的范围是128-255,符合一个字节的无符号表示。</think>### 封包算法解析 #### 正向运算公式(数值转封包) 1. 数值乘以2得到中间值:$N_{\text{中间}} = N_{\text{原始}} \times 2$ 2. 中间值拆分为商和余数: $$Q = \left\lfloor \frac{N_{\text{中间}}}{128} \right\rfloor,\quad R = N_{\text{中间}} \mod 128$$ 3. 封包高位字节:$H = R + 128$ 4. 封包低位字节:$L = Q$ 5. 最终封包格式:$[H][L]$(十六进制表示) #### 逆向运算公式(封包转数值) 1. 从高位字节提取余数:$R = H - 128$ 2. 从低位字节提取商:$Q = L$ 3. 组合中间值:$N_{\text{中间}} = Q \times 128 + R$ 4. 还原原始数值:$N_{\text{原始}} = \frac{N_{\text{中间}}}{2}$ #### 示例验证 **原始数值180的编码过程:** $$ \begin{align*} 180 \times 2 &= 360 \\ 360 \div 128 &= 2 \text{ 余 } 104 \\ H &= 104 + 128 = 232 \ (\text{0xE8}) \\ L &= 2 \ (\text{0x02}) \\ \text{封包} &= \text{E8 02} \end{align*} $$ **封包E8 02的解码过程:** $$ \begin{align*} H &= 232 \Rightarrow 232 - 128 = 104 \\ L &= 2 \Rightarrow 2 \times 128 = 256 \\ 256 + 104 &= 360 \\ 360 \div 2 &= 180 \end{align*} $$ --- ### 易语言实现代码 #### 正向运算(数值转封包) ```easy-lang .子程序 数值到封包, 字节集 .参数 数值, 整数型 .局部变量 中间值, 整数型 .局部变量 高位, 整数型 .局部变量 低位, 整数型 中间值 = 数值 × 2 低位 = 中间值 ÷ 128 高位 = (中间值 % 128) + 128 返回 (到字节集 (高位) + 到字节集 (低位)) ``` #### 逆向运算(封包转数值) ```easy-lang .子程序 封包到数值, 整数型 .参数 封包数据, 字节集 .局部变量 高位, 整数型 .局部变量 低位, 整数型 .局部变量 中间值, 整数型 高位 = 取字节集数据 (取字节集中间 (封包数据, 1, 1), #整数型) 低位 = 取字节集数据 (取字节集中间 (封包数据, 2, 1), #整数型) 中间值 = 低位 × 128 + (高位 - 128) 返回 (中间值 ÷ 2) ``` --- ### 算法特性说明 1. **位移压缩**:通过$\times 2$操作扩大数值范围,允许用更少字节存储较大数值[^4] 2. **余数偏移**:高位字节增加128的偏移量($R + 128$),确保字节值始终在$128 \leq H \leq 255$范围内 3. **兼容性**:支持最大数值$N_{\text{max}} = \frac{128 \times 255}{2} = 16320$的存储
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值