[noip模拟赛]二进制(dp)

来源:互联网 发布:诸葛诞 知乎 编辑:程序博客网 时间:2024/04/29 02:56

题目描述

这里写图片描述

题解

30分的话暴力就可以,100分考虑dp。
求出ABC中最长的len了之后,就是相当于XY都有len个空缺来填数,可以填0/1,要保证最终填出来的XY中1的个数分别和AB相同,X+Y的长度不超过len,并且X+Y中1的个数与C相同。
设f[i][j][k][l]表示填到第i个空,X中填了k个1,Y中填了l个1,X+Y的长度不超过len,并且X+Y中有j个1的最小的Z=X+Y。
那么转移就很好写了,预处理出来2的幂,每次用f[i-1][j][k][l],f[i-1][j][k-1][l]+mi[i-1],f[i-1][j][k][l-1]+mi[i-1],f[i-1][j][k-1][l-1]+2*mi[i-1]去更新f[i][?][k][l]。注意这里?表示的是要具体算一下更新的最小值Z中的1的个数。
还需要注意的就是由合法的状态转移,还有边界。

代码

#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define N 50#define inf 2147483647int T,A,B,C,Z,len,ans;int f[N][N][N][N],mi[N];struct hp{int len,sum;}a,b,c,z;inline void clear(){    A=B=C=Z=len=0;}inline hp dig(int x){    hp re;    int sum=0,len=0;    if (!x) return re=(hp){1,0};    for (;x;x>>=1)        sum+=x&1,len++;    return re=(hp){len,sum};}inline int Max(int a,int b,int c){    if (b>a) a=b;    if (c>a) a=c;    return a;}int main(){    freopen("binary.in","r",stdin);    freopen("binary.out","w",stdout);    scanf("%d",&T);    mi[0]=1; for (int i=1;i<=31;++i) mi[i]=mi[i-1]*2;    while (T--)    {        clear();        scanf("%d%d%d",&A,&B,&C);        a=dig(A),b=dig(B),c=dig(C);        len=Max(a.len,b.len,c.len);        for (int i=0;i<=35;++i)            for (int j=0;j<=35;++j)                for (int k=0;k<=35;++k)                    for (int l=0;l<=35;++l)                        f[i][j][k][l]=inf;        f[0][0][0][0]=0;        for (int i=1;i<=len;++i)            for (int j=0;j<=min(c.sum,i);++j)                for (int k=0;k<=min(a.sum,i);++k)                    for (int l=0;l<=min(b.sum,i);++l)                    {                        if (f[i-1][j][k][l]!=inf)                        {                            Z=f[i-1][j][k][l];                            z=dig(Z);                            if (z.len<=len) f[i][z.sum][k][l]=min(f[i][z.sum][k][l],Z);                        }                        if (k!=0&&f[i-1][j][k-1][l]!=inf)                        {                            Z=f[i-1][j][k-1][l]+mi[i-1];                            z=dig(Z);                            if (z.len<=len) f[i][z.sum][k][l]=min(f[i][z.sum][k][l],Z);                        }                        if (l!=0&&f[i-1][j][k][l-1]!=inf)                        {                            Z=f[i-1][j][k][l-1]+mi[i-1];                            z=dig(Z);                            if (z.len<=len) f[i][z.sum][k][l]=min(f[i][z.sum][k][l],Z);                        }                        if (k!=0&&l!=0&&f[i-1][j][k-1][l-1]!=inf)                        {                            Z=f[i-1][j][k-1][l-1]+2*mi[i-1];                            z=dig(Z);                            if (z.len<=len) f[i][z.sum][k][l]=min(f[i][z.sum][k][l],Z);                        }                    }        ans=inf;        for (int i=1;i<=len;++i)            ans=min(ans,f[i][c.sum][a.sum][b.sum]);        if (ans!=inf) printf("%d\n",ans);        else puts("-1");    }}

总结

1、看来字符串dp的套路还是很深啊。

1 0
原创粉丝点击