P4574 [CQOI2013]二进制A+B(dp数位+贡献)

https://www.luogu.com.cn/problem/P4574


设dp[i][j][k][t]表示,当前在第i位(从右往左数),C填了j个,A填了k个,B填了t个的能凑出满足条件的最小的数。

但是这题存在进位,所以我们还要开一维处理进位的情况。注意我们对答案的贡献只考虑手动给C的第i位填1才算贡献。

然后讨论,当前AB两位是否对C位产生进位。

考虑AB两位不进位:

1.A,B都是0,且后一位不进1=>现在这一位就是0
2.A,B都是0,且后一位进1=>现在这一位就是1
3.A,B有一个是1,且后一位不进1=>现在这一位就是1

 

考虑AB两位进位:

1.AB都是1,且后一位有进位

2.AB一个是1,且后一位有进位

3.AB都是1,且后一个没进位

然后跑记忆化的话方便边界情况

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=50;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar();	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL dp[maxn][maxn][maxn][maxn][2];
///注意只有主动填1时这个1才对答案有贡献
bool vis[maxn][maxn][maxn][maxn][2];
LL dfs(LL now,LL c,LL a,LL b,bool en){

        if(now==0){
            if(c==0&&a==0&&b==0&&en==0) return 0;
            else return 1e18;
        }
        if(vis[now][c][a][b][en]) return dp[now][c][a][b][en];
        vis[now][c][a][b][en]=true;
        LL res=1e18;
        if(en){///该位自身AB对C有进位
            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));
            if(c&&a&&b) res=min(res,dfs(now-1,c-1,a-1,b-1,1)+(1LL<<(now-1)));
        }
        else{///该位自身AB对C无进位
            res=min(res,dfs(now-1,c,a,b,0));
            if(c){
                res=min(res,dfs(now-1,c-1,a,b,1)+(1LL<<(now-1)));
                if(a){
                    res=min(res,dfs(now-1,c-1,a-1,b,0)+(1LL<<(now-1)));
                }
                if(b){
                    res=min(res,dfs(now-1,c-1,a,b-1,0)+(1LL<<(now-1)));
                }
            }

        }
        dp[now][c][a][b][en]=res;
        return dp[now][c][a][b][en];
}
int main(void){
        cin.tie(0);std::ios::sync_with_stdio(false);
        LL a,b,c;cin>>a>>b>>c;
        LL len1=0;LL len2=0;LL len3=0;
        LL tot1=0;LL tot2=0;LL tot3=0;
        while(a){ tot1+=(a&1); a>>=1;len1++; }
        while(b){ tot2+=(b&1); b>>=1;len2++; }
        while(c){ tot3+=(c&1); c>>=1;len3++; }
        LL len=max(len1,max(len2,len3));

        memset(dp,0x3f3f,sizeof(dp));

        dfs(len,tot3,tot1,tot2,0);

        if(dp[len][tot3][tot1][tot2][0]>=(1LL<<(len))){
            cout<<"-1"<<"\n";
        }
        else{
            cout<<dp[len][tot3][tot1][tot2][0]<<"\n";
        }
   return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值