BZOJ 3107 二进制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\)

Input

输入仅一行,包含三个整数\(a, b, c\)

Output

输出仅一行,为\(c’\)的最小值。

Sample Input

7 6 9

Sample Output

10

HINT

\(a,b,c \le 2^{30}\)

自己难得看出来一道高维的\(dp\)题目:\(f[i][j][k][l][m]\)表示前\(i\)位二进制数中,\(a’\)用了\(j\)个1,\(b’\)用了\(k\)\(1\),合成的\(c’\)在前\(i\)位中有\(l\)\(1\)
于是,根据二进制加法,我们可以得到\(dp\)方程:
\[f[i+1][j+1][k][l+1][0] = min(f[i+1][j+1][k][l+1][0],f[i][j][k][l][0] \mid (1 \ll i))\]
\[f[i+1][j+1][k][l][1] = min(f[i+1][j+1][k][l][1],f[i][j][k][l][1])\]
\[f[i+1][j][k+1][l+1][0] = min(f[i+1][j][k+1][l+1][0],f[i][j][k][l][0] \mid (1 \ll i))\]
\[f[i+1][j][k+1][l][1] = min(f[i+1][j][k+1][l][1],f[i][j][k][l][1])\]
\[f[i+1][j+1][k+1][l][1] = min(f[i+1][j+1][k+1][l][1],f[i][j][k][l][0])\]
\[f[i+1][j+1][k+1][l+1][1] = min(f[i+1][j+1][k+1][l+1][1],f[i][j][k][l][1] \mid (1 \ll i))\]
\[f[i+1][j][k][l+1][0] = min(f[i+1][j][k][l+1][0],f[i][j][k][l][1] \mid (1 \ll i))\]
\[f[i+1][j][k][l][0] = min(f[i+1][j][k][l][0],f[i][j][k][l][0])\]

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;

typedef long long ll;
#define maxn (35)
ll a,b,c,f[maxn][maxn][maxn][maxn][2];
int n,w1,w2,w3;

inline void ready()
{
    memset(f,0x7,sizeof(f));
    f[0][0][0][0][0] = 0;
    int cnt,i;
    for (i = a,cnt = 0;i;i >>= 1,++cnt) if (i & 1) ++w1;
    n = max(n,cnt);
    for (i = b,cnt = 0;i;i >>= 1,++cnt) if (i & 1) ++w2;
    n = max(n,cnt);
    for (i = c,cnt = 0;i;i >>= 1,++cnt) if (i & 1) ++w3;
    n = max(n,cnt);
}

inline void dp()
{
    for (int i = 0;i < n;++i)
        for (int j = 0;j <= i&&j <= w1;++j)
            for (int k = 0;k <= i&&k <= w2;++k)
                for (int l = 0;l <= i&&l <= w3;++l)
                {
                    if (j < w1)
                    {
                        if (l < w3) f[i+1][j+1][k][l+1][0] = min(f[i+1][j+1][k][l+1][0],f[i][j][k][l][0]|(1<<i));
                        f[i+1][j+1][k][l][1] = min(f[i+1][j+1][k][l][1],f[i][j][k][l][1]);
                    }
                    if (k < w2)
                    {
                        if (l < w3) f[i+1][j][k+1][l+1][0] = min(f[i+1][j][k+1][l+1][0],f[i][j][k][l][0]|(1<<i));
                        f[i+1][j][k+1][l][1] = min(f[i+1][j][k+1][l][1],f[i][j][k][l][1]);
                    }
                    if (j < w1&&k < w2)
                    {
                        f[i+1][j+1][k+1][l][1] = min(f[i+1][j+1][k+1][l][1],f[i][j][k][l][0]);
                        if (l < w3) f[i+1][j+1][k+1][l+1][1] = min(f[i+1][j+1][k+1][l+1][1],f[i][j][k][l][1]|(1<<i));
                    }
                    if (l < w3) f[i+1][j][k][l+1][0] = min(f[i+1][j][k][l+1][0],f[i][j][k][l][1]|(1<<i));
                    f[i+1][j][k][l][0] = min(f[i+1][j][k][l][0],f[i][j][k][l][0]);
                }
}

int main()
{
    freopen("3107.in","r",stdin);
    freopen("3107.out","w",stdout);
    scanf("%lld %lld %lld",&a,&b,&c);
    ready();
    dp();
    if (f[n][w1][w2][w3][0] > (1ll<<40)) printf("-1");
    else printf("%lld",f[n][w1][w2][w3][0]);
    fclose(stdin); fclose(stdout);
    return 0;
}

转载于:https://www.cnblogs.com/mmlz/p/4299873.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值