[CQOI2013]二进制A+B

[CQOI2013]二进制A+B

https://ac.nowcoder.com/acm/problem/19926

题号 NC19926
名称 [CQOI2013]二进制A+B
来源 [CQOI2013]

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

输入三个整数a, b, c,把它们写成无前导0的二进制整数。比如a=7, b=6, c=9,写成二进制为a=111, b=110, c=1001。接下来以位数最多的为基准,其他整数在前面添加前导0,使得a, b, c拥有相同的位数。比如在刚才的例子中,添加完前导0后为a=0111, b=0110, c=1001。最后,把a, b, c的各位进行重排,得到a’, b’, c’,使得a’+b’=c’。比如在刚才的例子中,可以这样重排:a’=0111, b’=0011, c’=1010。

你的任务是让c’最小。如果无解,输出-1。

样例

输入
7 6 9
输出
10


算法1

(暴搜 + 记忆化搜索优化 + 剪枝)

记得我上一次写暴搜,那还是我上一次写暴搜的时候

分析

拿到题没什么好的思路就只能想到暴搜

既然要暴搜,我们就要想好每次层需要维护什么状态以及,最终我们需要求什么

我们需要重排的二进制表示并使它们的和可以由重排得到

重排我们可以枚举每一位是0还是1,**所以我们需要分别记录可用的1的个数**(0也可以但是直觉上就想维护1)

然后怎么知道最后的答案能否被重排得到?

首先我们需要知道当的某一位确定之后当前位的结果是多少

加法除了考虑当前位还需要考虑上一位的进位所以**我们需要维护一个进位状态**,然后就能知道当前位的结果了

知道每一位的结果我们就知道,重排后的答案中1的个数的大小,s和中1的个数相同说明有解

我们也可以反向思考,如果当前位的结果是1,就将中1消耗掉一个,最后如果中的1恰好被消掉说明有解

所以**我们再维护一个变量表示中可用的1有多少**


dfs

我们定义

表示用个1和个0组成,用个1和个0组成, 由得到长度为,且含有个1的数的最小值

答案就是(分别表示中1的个数,为最大位数)


优化

可行性剪枝:

if(s < 0 || s > u) return INF;

如果当前中可消耗的1的个数为负数了或者在未来无法消耗完了我们可以提前退出

记忆化搜索

我们用表示当前状态是否访问过,用表示递归结束后的结果

如果表示访问过,直接返回

时间复杂度

参考文献

C++ 代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3fll;
const int N = 35;
LL f[N][N][N][N][3];
bool vis[N][N][N][N][3];
LL a,b,c;
int nums[5],len;

LL dfs(int u,int na,int nb,int s,int ca)
{
    LL &v = f[u][na][nb][s][ca];
    if(vis[u][na][nb][s][ca]) return v;
    vis[u][na][nb][s][ca] = true;
    if(s < 0 || s > u) return INF;
    if(u == 0)
    {
        if(ca == 1 || na != 0 || nb != 0 || s != 0) return INF;
        return f[u][na][nb][s][ca] = 0;
    }
    LL tmp;
    if(na >= 1 && nb >= 1)//a和b当前位都为1
    {
        tmp = dfs(u - 1,na - 1,nb - 1,s - ca,1);
        if(tmp < INF) tmp = tmp * 2ll + ca;
        v = min(v,tmp);
    }
    if(na >= 1)//a的当前为1,b为0
    {
        tmp = dfs(u - 1,na - 1,nb,s - (1 ^ ca),(1 + ca) >= 2);
        if(tmp < INF) tmp = tmp * 2ll + (1 ^ ca);
        v = min(v,tmp);
    }
    if(nb >= 1)//b的当前位为1,a为0
    {
        tmp = dfs(u - 1,na,nb - 1,s - (1 ^ ca),(1 + ca) >= 2);
        if(tmp < INF) tmp = tmp * 2ll + (1 ^ ca);
        v = min(v,tmp);
    }
    tmp = dfs(u - 1,na,nb,s - ca,0);//a,b当前位都为0
    if(tmp < INF) tmp = tmp * 2ll + ca;
    v = min(v,tmp);
    return v;
}

int main()
{
    scanf("%lld%lld%lld",&a,&b,&c);
    memset(f,0x3f,sizeof f);
    int res = 0;
    while(a)
    {
        nums[0] += (a & 1);
        a >>= 1;
        res ++;
    }
    len = max(len , res);
    res = 0;
    while(b)
    {
        nums[1] += (b & 1);
        b >>= 1;
        res ++;
    }
    len = max(len , res);
    res = 0;
    while(c)
    {
        nums[2] += (c & 1);
        c >>= 1;
        res ++;
    }
    len = max(len , res);
    LL t = dfs(len,nums[0],nums[1],nums[2],0);
    if(t >= INF) t = -1;
    printf("%lld\n",t);
    return 0;
}
全部评论
中间markdown显示好像有问题,但是我在本地typroa没问题,不知道怎么解决
点赞 回复 分享
发布于 2021-04-30 20:28

相关推荐

我见java多妩媚:大外包
点赞 评论 收藏
分享
废铁汽车人:秋招真是牛鬼蛇神齐聚一堂
点赞 评论 收藏
分享
oppo 应用软开 22*15+0.5*12
拿到了ssp完美:真的坎坷,但是你至少拿到这么多offer了!
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务