A+B Problem 详细解答 (转载)

此为详细装13版

转载自:https://vijos.org/discuss/56ff2e7617f3ca063af6a0a3

全文如下,未作修改,仅供围观,不代表个人观点:

你们怎么都在做网络流,不就是一道简单的递推吗   发表于2016-04-02 10:29

而且你们假惺惺的用网络流,过程中还是要用加法,我一个加法都没用。

#include <cstdio>
int m, n, a[32768][32768];
int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; ++i) {
        a[i][0] = i;
        for (int j = 1; j <= n; ++j) {
            a[0][j] = j;
            a[i][j] = ++a[i - 1][j];
            --a[i - 1][j];
        }
    }
    printf("%d\n", a[m][n]);
}

根据加法的性质,0 为加法单元,满足 m + 0 = m, 0 + n = n
然后就是裸推了:i + j = i + (j - 1) + 1

但是这样会超时,而且在 Vijos 上测数组太大了,编译就错误了。所以要进行优化,合并一个状态:
设 F(i) = i + n, 则 F(0) = n, F(i) = F(i - 1) + 1

#include <cstdio>
int m, n, a[32768];
int main()
{
    scanf("%d%d", &m, &n);
    a[0] = n;
    for (int i = 1; i <= m; ++i) {
        a[i] = ++a[i - 1];
        --a[i - 1];
    }
    printf("%d\n", a[m]);
}

此时已经可以通过了,然而,本着精益求精的态度,进一步可以用滚动数组优化,变成这样:

#include <cstdio>
int m, n, ans;
int main()
{
    scanf("%d%d", &m, &n);
    ans = n;
    while (m--) ++ans;
    printf("%d\n", ans);
}

这是递推做法的最优解了。然而,事实上,还可以用位运算做,才是真正的最优解。
首先,加法分为两个步骤,一个是数字加,一个是进位。
因为单位二进制中 1 + 1 = 0, 1 + 0 = 1, 0 + 0 = 0, 0 + 1 = 1
正好符合异或的性质。
进位的部分则为 a & b。
但是第一位不可能进位,所以整体移动一位,即 (a & b) << 1.
那么 a + b = (a ^ b) + ((a & b) << 1);
出现了加号!可是这是可以递归的,故程序优化如下:

#include <cstdio>
int m, n;
int add(int a, int b)
{
    if (a == 0) return b;
    if (b == 0) return a;
    int s = a ^ b;
    int t = (a & b) << 1;
    return add(s, t);
}
int main()
{
    scanf("%d%d", &m, &n);
    printf("%d\n", add(m, n));
}

显然,该程序时间复杂度为 Ø(log max{a, b})
因为这是一个尾递归,所以我们可以通过迭代消除它。

#include <cstdio>
int m, n;
int main()
{
    scanf("%d%d", &m, &n);
    int u = m & n;
    int v = m ^ n;
    while (u) {
        int s = v;
        int t = u << 1;
        u = s & t;
        v = s ^ t;
    }
    printf("%d\n", v);
}

即为本题最优解。
在 Vijos 上看不出差距,在洛谷上,位运算解法 2ms 通过,递推的最优解不仅时间很长,还超时了一个点。

不得不说,本题很考察思维,一步一步优化,到达最优。

时间: 2024-12-12 11:51:20

A+B Problem 详细解答 (转载)的相关文章

2016年上半年网络工程师考试下午真题,参考答案,详细解答发布

2016年上半年网络工程师考试下午真题,参考答案,详细解答发布http://down.51cto.com/data/2221024

数据结构:可以用求最短路径的方法思想求最长路径么?给出详细解答。。

数据结构:可以用求最短路径的方法思想求最长路径么?为什么呢? 这里求解最短路径的通用方法有Dijkstra算法和Floyd-Warshall算法,Dijkstra算法不允许边的权值为负,也不允许有回路,而Floyd-Warshall算法可以允许边的权值为负,但不允许负值边构成回路,即可以求解有回路的图 它们都有局限,这两种算法的思想可以用来求最长路径么?? 为什么 不可以? (感谢给我答案的好心人:来自于知乎:http://www.zhihu.com/question/27201255和CSDN

微信送礼物投票系统的详细解答

就目前来说,市场上的第三方微信投票系统种类很多,功能不一鱼龙混杂,功能很多的情况下有一些细微的区别,对于用户来说选择有一定的难度,下面我就来简单介绍一下微信投票活动大家经常关注的16个问题,对此进行详细的解答:只要能同时包括这个些功能的系统,通常都能够很好的满足活动举办方的要求,活动良好的用户体验! Q1.该投票系统可以设置每个微信用户投票次数吗? A3:可以的,可以设置一次活动每个微信用户的投票数,可设置每个微信用户每天的投票数!并且取消关注自动减掉此用户投票的所有记录,做到了自动减票的功能.

android4.0蓝牙使能的详细解析 (转载)

此博客是转载过来的哦... 给自己博客定几个部分: (1)写在前面的话:一些写博客时的废话. (2)内容简介:把文章的主要内容或者核心部分作一个框架性的概括,以方便大家阅读. (3)正文:这个不需要解释了.   写在前面的话:这是csdn上的第一篇博客,希望自己能够坚持写下去,也希望能够得到大家的支持.本文可能会涉及大量的源码注释,在文字方面可能不够尽如人意,希望真正想理解该过程的同学们能够耐心看下去. 内容简介:本文详细分析了android4.0中蓝牙使能的过程,相比较android2.3,4

mysql编译安装(详细)(转载)

mysql编译安装(详细) 一.编译安装MySQL前的准备工作 安装编译源码所需的工具和库 yum install gcc gcc-c++ ncurses-devel perl 安装cmake,从http://www.cmake.org下载源码并编译安装 wget http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz tar -xzvf cmake-2.8.10.2.tar.gz cd cmake-2.8.10.2 ./bootstrap ;

【字符编码】Java字符编码详细解答及问题探讨

一.前言 继上一篇写完字节编码内容后,现在分析在Java中各字符编码的问题,并且由这个问题,也引出了一个更有意思的问题,笔者也还没有找到这个问题的答案.也希望各位园友指点指点. 二.Java字符编码 直接上代码进行分析似乎更有感觉.   运行结果:   说明:通过结果我们知道如下信息. 1. 在Java中,中文在用ASCII码表示为3F,实际对应符号'?',用ISO-8859-1表示为3F,实际对应符号也是为'?',这意味着中文已经超出了ASCII和ISO-8859-1的表示范围. 2. UTF

github版本库使用详细教程(转载)

教程[1] 同学们,上课了!今天我们来学习github的使用,我们将用它来管理我们的代码,你会发现它的好处的,当然是要在本系列教程全部完成之后,所以请紧跟站长的步伐,今天是第一天,我们来学习如何在git上建立自己的版本仓库,并将代码上传到仓库中. 一.注册一个免费git帐号,可以无限建立访问权限为公共的版本仓库, 注册地址:https://github.com/signup/free 二.依次填写,用户名[昵称]..邮箱地址[用来登录].密码, 点击creat an account进行注册 三.

argc argv 详细解答

转载:http://www.cnblogs.com/avril/archive/2010/03/22/1691477.html argc是命令行总的参数个数     argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数 int main(int argc,char* argv[])详解 argc是命令行总的参数个数     argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数     命令行后面跟的用户输入的参数,比如:     int   main(int   

C语言运算符优先级列表(超级详细的---转载加自己习题)

转载于:http://www.jb51.net/article/37282.htm 每当想找哪个运算符优先级高时,很多时候总是想找的就没有,真让人气愤!现在,终于有个我个人觉得非常全的,分享给大家,欢迎拍砖! C语言运算符优先级 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 -- () 圆括号 (表达式)/函数名(形参表) -- . 成员选择(对象) 对象.成员名 -- -> 成员选择(指针) 对象指针->成员名 -- 2 - 负号运算