11 solutions
-
7
首先是一个不那么正经的解法
我们考虑到这题的数据只有 1024,那么,我们考虑小学时老师教你的:
你有 2 个苹果,然后你又拿到了 3 个苹果,最后你有 5 个苹果,所以 那加法就完成了(代码中的
ushort是unsigned short)short $(ushort x, ushort y) { return std::string().append(x, '.').append(y, '.').length(); }那么减法呢?我们知道,,在计算机中,,so,,调用之前写的加法就好:
short _(short x, short y) { y = ~y; return $($(x, reinterpret_cast<ushort&>(y)), 1); }最后乘法,还是考虑小学的时候老师教你的:
就是 个 相加。
还是用
string。我们往一个空的string里面塞 次长度为 的字符串,然后就好了。
但是我们发现一个问题:我们既不能for (int i = 0;i < y;i++),也不能while (y--)。这可怎么办呢?还是用string。我们每循环一次就往一个初始为空的字符串里塞一个字符,这个串有多长我们就循环了多少次。然后,乘法也被淦掉了:int X(short x, short y) { std::string str = ""; std::string tt = std::string().append(x, ' '); std::string res = ""; while (str.append(1, ' ').length() <= y) { res.append(tt); } return res.length(); }最后是这种做法的完整代码:
#include <iostream> #include <string> #define ushort unsigned short short $(ushort x, ushort y) { return std::string().append(x, '.').append(y, '.').length(); } short _(short x, short y) { y = ~y; return $($(x, reinterpret_cast<ushort&>(y)), 1); } int X(short x, short y) { std::string str = ""; std::string tt = std::string().append(x, ' '); std::string res = ""; while (str.append(1, ' ').length() <= y) { res.append(tt); } return res.length(); } int main() { int x, y; std::cin >> x >> y; std::cout << $(x, y) << ' ' << _(x, y) << ' ' << X(x, y) << std::endl; }
玩够了不着调的瞎搞做法,咱回过头来瞅瞅如何用位运算解题。(三年后的补档)
正经的解法
加法
咱先研究俩一位数相加的情况,有:
- 0 + 0 = 00
- 1 + 0 = 01
- 0 + 1 = 01
- 1 + 1 = 10
注意到: 与 相加,高位为 ,低位为 。
然后咱考虑进位,有:
- 0 + 0 + 1 = 00 + 1 = 01
- 1 + 0 + 1 = 01 + 1 = 10
- 0 + 1 + 1 = 01 + 1 = 10
- 1 + 1 + 1 = 10 + 1 = 11
发现实际上进位实际上可以作为一单独过程处理,而不影响先前的结论。(事实上全加器就是可以由多个半加器拼合而成的)
现在拓展到任意二进制数,以 7 + 14 为例:
0 1 1 1 (7) + 1 1 1 0 (14) -------------- [1][1][1][0] (carry bits) -------------- 1 0 1 0 1 (21)显然要是像咱平时列竖式那样一位一位整有点麻烦。(其实也不麻烦)
但基于我们之前的结论:进位可以作为一个单独过程处理。有啥用呢?我们将进位与异或分离处理,则:
0 1 1 1 (7) + 1 1 1 0 (14) -------------- 1 0 0 1 (9) c:0 1 1 0 (12) -------------- 0 0 1 0 1 (5) c:1 0 0 (16) -------------- 1 0 1 0 1 (21) c:0 0 (0)其中每一次变换的第一行都是上一组的两个值按位异或的结果;第二行(carry)为上一组两个值按位与并左移一位的结果。注意到:
- 每一组数的和不变;这保证结果正确
- 进位(carry)数的有效位数变短;这保证进位最终变为 0(程序能够结束)
部分代码:
short add(ushort x, ushort y) { while (y != 0) { int r = x & y; x ^= y; y = r << 1; } return x; }(实际上这种方式最差需要 ,与按位单独计算相同,但比咱之前瞎搞做法的 好)
减法
利用补码,原理相同,不再赘述
部分代码:
short sub(short x, short y) { y = ~y; return add(add(x, reinterpret_cast<ushort&>(y)), 1); }乘法
显而易见的有一种想法就是, 就是 个 相加。这种方式 次相加效率较低。既然正经做法,咱想想是否有更快的方式呢?
可以从快速幂中借鉴思路。快速幂解决的是 ,即 个 相乘的问题。其核心是将指数 分解:
得到结果 :
其中 可以写作 ,其中 可在 时间内得到,而 仅有 0(使结果便为 1)与 1(保持结果不变)两种情况,从而实现快速
而咱所要解决的乘法是 ,即 个 相乘的问题。同样将 分解后,所要得到的结果 :
$\displaystyle r = \sum_{n=0}^{\infty} a \times {2^n b_n}$
相似的, 可以写作 ,其中 可在 时间内得到,而 仅有 0(使结果便为 0)与 1(保持结果不变)两种情况。你问乘 2 咋整?左移。
部分代码:
int mul(short x, short y) { int res = 0; while (y != 0) { if ((y & 1) == 1) { res = add(x, res); } x <<= 1; y >>= 1; } return res; }最终代码实现:
#include <iostream> #include <string> typedef unsigned short ushort; short add(ushort x, ushort y) { while (y != 0) { int r = x & y; x ^= y; y = r << 1; } return x; } short sub(short x, short y) { y = ~y; return add(add(x, reinterpret_cast<ushort&>(y)), 1); } int mul(short x, short y) { int res = 0; while (y != 0) { if ((y & 1) == 1) { res = add(x, res); } x <<= 1; y >>= 1; } return res; } int main() { int x, y; std::cin >> x >> y; std::cout << add(x, y) << ' ' << sub(x, y) << ' ' << mul(x, y) << std::endl; }
Information
- ID
- 246
- Time
- 1000ms
- Memory
- 256MiB
- Difficulty
- 2
- Tags
- (None)
- # Submissions
- 339
- Accepted
- 77
- Uploaded By