[bzoj3107][CQOI2013]二进制a+b

43 篇文章 0 订阅
5 篇文章 3 订阅

题目大意及模型转换

给定三个二进制数a,b,c。对每个数进行重组变为a’,b’,c’。你需要满足a’+b’=c’,并令c’最小。若无解输出-1。
a,b,c<= 231

考虑简化

其实,我们发现有用的东西只是a,b,c的最大位数(决定了答案最多可以多少位,注意这里的位数是十进制下的,那么最小的答案都超过极限位数证明输出-1),以及a,b,c中1的个数(记为x,y,z)。为方便讨论,我们应当规定x>=y。

分类讨论

现在我们考虑这样一个子问题。
设为 solve(x,y,z,p) 表示第一个数有x个1,第二个数有y个1,得到的数需要有z个1,其中最低位相加时要加上进位的p=0..1。

z=1

很显然了,举个例子x=10,y=5,z=1。
当p=0时:

000001111111111
011110000000001
100000000000000
最优策略是把x个1都堆在后面,最低位放1,然后接下来再放y-1个1,使得c’最高位为1。
所以 solve(x,y,z,0)=2x+y1

当p=1时:

0000001111111111
0111110000000000
1000000000000000
最优策略是把x个1都堆在后面,但因为有进位,所以最低位不能放1,然后再往前放y个1,同样令c’最高位为1。
所以 solve(x,y,z,1)=2x+y

所以 solve(x,y,z,p)=2x+y1+p

z=y

设x=10,y=5,z=5。

当p=0时:
01111111111
00000011111
10000011110

显然这种情况保证a’,b’最小会最优。因为a’,b’最小时刚好c’有y个1,所以合法。又因为a’,b’最小,所以c’也最小。
所以 solve(x,y,z,0)=2x+2y2

当p=1时:
001111111111
010000011110
100000011110

这种情况我们只能把b’最低位变成0,然后在第x+1位补上一个1,使得c’的最高位的1落在第x+2位。
所以 solve(x,y,z,1)=2x+1+2y11

所以 solve(x,y,z,p)=2x+p+2yp2+p

1<z<y

假设x=10,y=5,z=3。
有点不好搞。我们来看看最低位应该怎么弄,然后可以继续将它分成下一个子问题。先考虑p=0。

如果最低位是1+0=1,那么下一个子问题将会变为 solve(x1,y,z1,0)
按照策略会继续减,一直到z=1时。
那么答案自然是 2x+yz2z1+2z11

如果最低位是0+1=1,那么下一个子问题将会变为 solve(x,y1,z1,0)
按照策略已知道z=1。
那么答案自然是 2x+yz2z1+2z11

两种一样,我们合并。

如果最低位是1+1=0,那么下一个子问题将会变为 solve(x1,y1,z,0)
按照策略会一直到y=z时。
那么答案自然是 2xy+z2z1+2z2z122z1

我们进行比较。
第一、二种策略: 2z1(2x+yz+1)1
第三种策略: 2z1(2xy+z+2z2)
由于我们知道y>z。那么显然第三种策略更优。

p=1可以研究一下,还是1+1最优。

因此可以得知。
solve(x,y,z,p)=2solve(x1,y1,zp,1)+p

y<z<=x

x=10,y=5,z=8,p=0。
01111111111
00011111000
10011110111
这种情况不需要管进位。
因为初始状态p=0,而这种情况只有初始情况才会出现。
在c’中如果它的1跑的比较后面,它就会比较小。
因此最后z-y位都应该是1+0=1。
直至缩到y=z。
所以 solve(x,y,z,p)=2zysolve(x+yz,y,y,0)+2zy1

x<z<=x+y

x=10,y=5,z=12,p=0。
0111111111100
0111000000011
1110111111111
这种情况同样不需要理进位。
同样我们使c’的1弄到后面去。
因此最后z-x位都应该是0+1=1。
所以 solve(x,y,z,p)=2zxsolve(x,y+xz,x,0)+2zx1

z=x+y

不需要理进位。
x=10,y=5,z=15,p=0。
000001111111111
111110000000000
111111111111111

显然可得。
solve(x,y,z,p)=2x+y

不是以上情况中任意一种

solve(x,y,z,p)=-1

注意

最终得到的答案应该和最大位比较。

参考代码

(写的很优美呢)

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define two(x) (1<<x)
using namespace std;
int i,j,k,l,t,n,m,ans,a,b,c,x,y,z;
int lowbit(int x){
    return x&-x;
}
int getone(int x){
    int y=0;
    while (x){
        y++;
        x-=lowbit(x);
    }
    return y;
}
int getws(int x){
    int y=0;
    while (x){
        y++;
        x/=2;
    }
    return y;
}
int solve(int x,int y,int z,int p){
    if (z==1) return two(x+y+p-1);
    else if (z>1&&z<y) return 2*solve(x-1,y-1,z-p,1)+p;
    else if (z==y) return two(x+p)+two(y-p)-2+p;
    else if (z>y&&z<=x) return two(z-y)*solve(x+y-z,y,y,0)+two(z-y)-1;
    else if (z>x&&z<x+y) return two(z-x)*solve(x,y+x-z,x,0)+two(z-x)-1;
    else if (z==x+y) return two(z)-1;
    else return -1;
}
int main(){
    scanf("%d%d%d",&a,&b,&c);
    x=getone(a);y=getone(b);z=getone(c);
    n=max(max(getws(a),getws(b)),getws(c));
    if (x<y) swap(x,y);
    ans=solve(x,y,z,0);
    if (getws(ans)>n) ans=-1;
    printf("%d\n",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值