您的位置:首页 > 其它

bzoj 3107: [cqoi2013]二进制a+b 数位dp

2017-09-23 21:20 302 查看

题意

输入三个整数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。

a,b,c<=2^30

分析

很容易想到dp:f[i,j,k,l,0/1]表示从低位到高位的前i位中,a’用了j个1,b’用了k个1,c’用了l个1,且对c’的下一位是否有进位时c’的最小值。

直接暴力转移即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 0x7fffffff
using namespace std;

int bin[35],a,b,c,f[35][35][35][35][2];

void updata(int &d,int x)
{
d=min(d,x);
}

int main()
{
bin[0]=1;
for (int i=1;i<=30;i++) bin[i]=bin[i-1]*2;
scanf("%d%d%d",&a,&b,&c);
int len=0,s1=0,s2=0,s3=0;
for (int i=0;i<=30;i++)
{
if (a&bin[i]) s1++,len=i+1;
if (b&bin[i]) s2++,len=i+1;
if (c&bin[i]) s3++,len=i+1;
}
for (int i=0;i<=len;i++)
for (int j=0;j<=s1;j++)
for (int k=0;k<=s2;k++)
for (int l=0;l<=s3;l++)
f[i][j][k][l][0]=f[i][j][k][l][1]=inf;
f[0][0][0][0][0]=0;
for (int i=0;i<len;i++)
for (int j=0;j<=s1;j++)
for (int k=0;k<=s2;k++)
for (int l=0;l<=s3;l++)
{
if (f[i][j][k][l][0]<inf)
{
updata(f[i+1][j][k][l][0],f[i][j][k][l][0]);
if (j<s1&&l<s3) updata(f[i+1][j+1][k][l+1][0],f[i][j][k][l][0]+bin[i]);
if (k<s2&&l<s3) updata(f[i+1][j][k+1][l+1][0],f[i][j][k][l][0]+bin[i]);
if (j<s1&&k<s2) updata(f[i+1][j+1][k+1][l][1],f[i][j][k][l][0]);
}
if (f[i][j][k][l][1]<inf)
{
if (l<s3) updata(f[i+1][j][k][l+1][0],f[i][j][k][l][1]+bin[i]);
if (j<s1) updata(f[i+1][j+1][k][l][1],f[i][j][k][l][1]);
if (k<s2) updata(f[i+1][j][k+1][l][1],f[i][j][k][l][1]);
if (j<s1&&k<s2&&l<s3) updata(f[i+1][j+1][k+1][l+1][1],f[i][j][k][l][1]+bin[i]);
}
}
if (f[len][s1][s2][s3][0]==inf) puts("-1");
else printf("%d",f[len][s1][s2][s3][0]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: