BZOJ3107: [cqoi2013]二进制a+b

138 篇文章 0 订阅

可以把问题转换成在 202n 里选a个数,再在里面选b个数,使他们的和在二进制下有c位且最高位不大于 2n ,使这个和最小
然后可以DP,从 20 开始选, f[i][j][l][k][0] 表示选了前 i 位,a个数里面选了j个,b个数里面选了 l 个,和有k位,无进位,最后一位是1就代表有进位,然后DP一下就行了

后记:这题原来可以构造的,Orz PoPoQQQ


code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn = 35;
int f[maxn][maxn][maxn][maxn][2];
int n,a,b,c;
int an,bn,cn;

void up(int &x,int y){if(y>x)x=y;}
void down(int &x,int y){if(y<x)x=y;}

int main()
{
    an=bn=cn=0;
    scanf("%d%d%d",&a,&b,&c);
    int tmp=0; n=0;
    while(a)
    {
        if(a&1)an++;    a>>=1;
        tmp++;
    } up(n,tmp); tmp=0;
    while(b)
    {
        if(b&1)bn++;    b>>=1;
        tmp++;
    } up(n,tmp); tmp=0;
    while(c)
    {
        if(c&1)cn++;    c>>=1;
        tmp++;
    } up(n,tmp); tmp=0;

    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            for(int l=0;l<=n;l++)
                for(int k=0;k<=n;k++)
                    f[i][j][l][k][0]=f[i][j][l][k][1]=INT_MAX;

    for(int i=0;i<=min(an,1);i++)
    {
        for(int j=0;j<=min(bn,1);j++)
        {
            f[0][i][j][i^j][i&j]=i+j;
            //printf("0 %d %d %d %d %d\n",i,j,i^j,i&j,i+j);
        }
    }
    for(int i=1;i<n;i++)
    {
        int ap=min(i,an),bp=min(i,bn);
        for(int j=0;j<=ap;j++)
            for(int l=0;l<=bp;l++)
            {
                for(int k=0;k<=cn;k++)
                {
                if(f[i-1][j][l][k][0]!=INT_MAX)
                {
                    int temp=f[i-1][j][l][k][0];
                    // 0 0
                    down(f[i][j][l][k][0],temp);
                    // 0 1
                    if(l<bn)
                        down(f[i][j][l+1][k+1][0],temp+(1<<i));
                    // 1 0
                    if(j<an)
                        down(f[i][j+1][l][k+1][0],temp+(1<<i));
                    // 1 1
                    if(j<an&&l<bn)
                        down(f[i][j+1][l+1][k][1],temp+(1<<i+1));
                }
                if(f[i-1][j][l][k][1]!=INT_MAX)
                {
                    int temp=f[i-1][j][l][k][1];
                    //0 0
                    down(f[i][j][l][k+1][0],temp);
                    //0 1
                    if(l<bn)
                    down(f[i][j][l+1][k][1],temp+(1<<i));
                    //1 0
                    if(j<an)
                    down(f[i][j+1][l][k][1],temp+(1<<i));
                    //1 1
                    if(j<an&&l<bn)
                    down(f[i][j+1][l+1][k+1][1],temp+(1<<i+1));
                }
            //printf("%d %d %d %d %d %d\n",i,j,l,k,0,f[i][j][l][k][0]);
            //printf("%d %d %d %d %d %d\n",i,j,l,k,1,f[i][j][l][k][1]);
                }
            }
    }
    int ret=f[n-1][an][bn][cn][0];
    if(ret==INT_MAX) printf("-1\n");
    else printf("%d\n",ret);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值