一道数位dp题,状态有点复杂但是不难

补个范围:

题目大意:
给你三个数:a,b,c
你可以对这三个数的二进制进行任意排列使得最终满足:c=a+b
求最小的满足条件的c,如果没有输出-1
这道题,我们考虑先计算出a,b,c各种的二进制中有多少个"1",分别设为:A,B,C
在算出三个数中二进制长度最长的,设为n
那么,现在题目就相当于:
分别用A,B,C个1凑出长度为n的三个二进制数(允许前导0)满足上述和的关系,且要求c最小

于是我们就可设int 表示,我们还剩i位,C剩j个1,A剩k个1,B剩t个1能凑出满足条件的最小的数来

但,我们注意到,题目是“加法”,于是自然而然的产生了一种情况——进位,如果我们dp这么设的话,是无法保证答案正确的,所以我们再开一位:表示我们还剩i位,C剩j个1,A剩k个1,B剩t个1并且我们是否要向前1位进1(因为最大情况就是1+1+1(本身两个1加上后面进的1))

一开始有:

那么,我们就来考虑转换:
如果,我们现在不向前一位进1,那么有如下情况:
1.A,B都是0,且后一位不进1=>现在这一位就是0
2.A,B都是0,且后一位进1=>现在这一位就是1
3.A,B有一个是1,且后一位不进1=>现在这一位就是1
于是我们有转移方程:
(当然,如果j/k/t等于0的话,那么j/k/t就不存在需要-1的情况了)
同理,我们有向前一位进1的转移方程:
这样,我们就可以直接推出所有的dp的值了
按照意义,答案就是
复杂度
可以通过此题
代码(我打的记搜,2333):

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=50,inf=1e15;
long long sav[N];
long long dp[N][N][N][N][2];
bool vis[N][N][N][N][2];
inline int dfs(int now,int C,int A,int B,bool ned){
    if(!now){
       if(C==0&&A==0&&B==0&&ned==0){
           return 0;
       }
       return inf;
    }
    if(vis[now][C][A][B][ned]){
        return dp[now][C][A][B][ned];
    }
    vis[now][C][A][B][ned]=1;
    int res=inf;
    if(ned){
        //0
        if(A&&B){
            res=min(res,dfs(now-1,C,A-1,B-1,0));
        }
        if(A){
            res=min(res,dfs(now-1,C,A-1,B,1));
        }
        if(B){
            res=min(res,dfs(now-1,C,A,B-1,1));
        }
        //1
        if(C){
            if(A&&B){
                res=min(res,dfs(now-1,C-1,A-1,B-1,1)+sav[now]);
            }
        }
    }else{
        //0
        res=min(res,dfs(now-1,C,A,B,0));
        //1
        if(C){
            res=min(res,dfs(now-1,C-1,A,B,1)+sav[now]);
            if(A){
                res=min(res,dfs(now-1,C-1,A-1,B,0)+sav[now]);
            }
            if(B){
                res=min(res,dfs(now-1,C-1,A,B-1,0)+sav[now]);
            }
        }
    }
    return dp[now][C][A][B][ned]=res;
}
signed main(){
    sav[1]=1;
    for(int i=2;i<=49;++i){
        sav[i]=(sav[i-1]<<1);
    }
    long long a,b,c;
    scanf("%lld%lld%lld",&a,&b,&c);
    int A=0,B=0,C=0,tot=0,tol=0;
    while(a){
        A+=(a&1);a>>=1;++tot;
    }
    while(b){
        B+=(b&1);b>>=1;++tol;
    }
    tot=max(tot,tol),tol=0;
    while(c){
        C+=(c&1);c>>=1;++tol;
    }
    tot=max(tot,tol);
    memset(dp,0x3f3f,sizeof(dp));
    dfs(tot,C,A,B,0);
    if(dp[tot][C][A][B][0]>=sav[tot+1]){
        puts("-1");
    }else{
        printf("%lld",dp[tot][C][A][B][0]);
    }
    return 0;
}