3202. 【CQOI2013】二进制a+b
Description
输入三个整数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。
Solution
考虑 dp。
先统计出 \(a\),\(b\),\(c\) 里 1 的个数,然后统计出最多有多少位 \(n\)。
设 \(f_{i,x,y,z,0/1}\) 表示到了二进制下的第 \(i\) 位,\(a\),\(b\),\(c\) 分别用了 \(x,y,z\) 个 1,上一位是否进位的最小的 \(c'\)。
因为是最小值,所以初值为 \(\inf\)。
转移(我是采用由当前往下个结果转移):
\[f_{i+1,x,y,z,0}=\min(f_{i+1,x,y,z,0},f_{i,x,y,z,0})\\
f_{i+1,x+1,y,z+1,0}=\min(f_{i+1,x+1,y,z+1,0},f_{i,x,y,z,0}+2^i)\\
f_{i+1,x,y+1,z+1,0}=\min(f_{i+1,x,y+1,z+1,0},f_{i,x,y,z,0}+2^i)\\
f_{i+1,x+1,y+1,z+1,1}=\min(f_{i+1,x+1,y+1,z+1,1},f_{i,x,y,z,0}+2^{i+1})\\
f_{i+1,x,y,z,0}=\min(f_{i+1,x,y,z,0},f_{i,x,y,z,1})\\
f_{i+1,x+1,y,z,1}=\min(f_{i+1,x+1,y,z,1},f_{i,x,y,z,1}+2^i)\\
f_{i+1,x,y+1,z,1}=\min(f_{i+1,x,y+1,z,1},f_{i,x,y,z,1}+2^i)\\
f_{i+1,x+1,y+1,z+1,1}=\min(f_{i+1,x+1,y+1,z+1,1},f_{i,x,y,z,1}+2^{i+1})
\]
首先前 4 个转移是没有进位的,第 1 个转移是都不放 1,就直接转移。
第 2、3 个转移是放一个 1,就需要加上 \(2^i\)。
第 4 个转移是放两个 1,此时就会进位,而且是加上 \(2^{i+1}\) 而不是 \(2^i\)。
后面 4 个转移同理,注意下第 5、6 个转移是不需要令 \(z\) 加 1 的,因为只放一个是不影响 \(c\) 中 1 的个数的。
Code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50
#define inf 0x7f7f7f7f
#define ll long long
using namespace std;
int numa,numb,numc,n;
ll a,b,c,f[N][N][N][N][3];
int lowbit(int x)
{
int res=0;
while (x)
{
res+=(x&1);
x>>=1;
}
return res;
}
int main()
{
scanf("%lld%lld%lld",&a,&b,&c);
n=max((int)log2(a)+1,(int)log2(b)+1);
n=max(n,(int)log2(c)+1);
memset(f,inf,sizeof(f));
numa=lowbit(a);
numb=lowbit(b);
numc=lowbit(c);
f[0][0][0][0][0]=0;
for (int i=0;i<n;++i)
for (int x=0;x<=numa;++x)
for (int y=0;y<=numb;++y)
for (int z=0;z<=numc;++z)
{
ll t=f[i][x][y][z][0];
f[i+1][x][y][z][0]=min(f[i+1][x][y][z][0],t);
f[i+1][x+1][y][z+1][0]=min(f[i+1][x+1][y][z+1][0],t+(1<<i));
f[i+1][x][y+1][z+1][0]=min(f[i+1][x][y+1][z+1][0],t+(1<<i));
f[i+1][x+1][y+1][z+1][1]=min(f[i+1][x+1][y+1][z+1][1],t+(1<<(i+1)));
t=f[i][x][y][z][1];
f[i+1][x][y][z][0]=min(f[i+1][x][y][z][0],t);
f[i+1][x+1][y][z][1]=min(f[i+1][x+1][y][z][1],t+(1<<i));
f[i+1][x][y+1][z][1]=min(f[i+1][x][y+1][z][1],t+(1<<i));
f[i+1][x+1][y+1][z+1][1]=min(f[i+1][x+1][y+1][z+1][1],t+(1<<(i+1)));
}
if (f[n][numa][numb][numc][0]>=inf) printf("-1\n");
else printf("%lld\n",f[n][numa][numb][numc][0]);
return 0;
}