P4574 [CQOI2013]二进制A+B
传送门
思路:
本题可用数位DP来做,设 f [ i ][ a ][ b ][ c ][ j ] 表示当前枚举到(二进制下的)第i位,a' b' c'各用a,b,c了几个1,j表示最后一位是否有进位。转移方程就只要暴力枚举8种情况(不同位置及是否进位)。
DP方程:
inline void dp()//动态规划,强行枚举八种情况 { f[0][0][0][0][0]=0; for (int i=0;i<n;++i) for (int j=0;j<=jla;++j) for (int k=0;k<=jlb;++k) for (int l=0;l<=jlc;++l) { long long tmp=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],tmp+(1<<i+1)); f[i+1][j+1][k][l+1][0]=min(f[i+1][j+1][k][l+1][0],tmp+(1<<i)); f[i+1][j][k+1][l+1][0]=min(f[i+1][j][k+1][l+1][0],tmp+(1<<i)); f[i+1][j][k][l][0]=min(f[i+1][j][k][l][0],tmp); tmp=f[i][j][k][l][1];//枚举最后一位进位的情况 f[i+1][j+1][k+1][l+1][1]=min(f[i+1][j+1][k+1][l+1][1],tmp+(1<<i+1)); f[i+1][j][k+1][l][1]=min(f[i+1][j][k+1][l][1],tmp+(1<<i)); f[i+1][j+1][k][l][1]=min(f[i+1][j+1][k][l][1],tmp+(1<<i)); f[i+1][j][k][l][0]=min(f[i+1][j][k][l][0],tmp); } }
由上DP转移方程可看出,如果是枚举最后一位进位的情况,则需要转移到不进位的DP方程。因为二进制是逢二进一,最后一位如果为1,且进位,就要变为0;如果是从不进位开始转移,则与进位相反。
完整代码:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define INF 0x7f7f7f7f7f7f7f int T,a,b,c; int n,jla,jlb,jlc;//n记录三个数的二进制数码长度的最大值,jla、jlb、jlc分别记录a、b、c的二进制数码中 1 的个数 long long f[33][33][33][33][2]; inline int read()//快读 { char kr=0; char ls; for(;ls>'9'||ls<'0';kr=ls,ls=getchar()); int xs=0; for(;ls>='0'&&ls<='9';ls=getchar()) { xs=xs*10+ls-48; } if(kr=='-') xs=0-xs; return xs; } inline int lowbit(int x)//求出x的二进制数码中 1 的个数 { int sum=0; for (;x;x>>=1) sum+=x&1; return sum; } inline void dp()//动态规划,强行枚举八种情况 { f[0][0][0][0][0]=0; for (int i=0;i<n;++i) for (int j=0;j<=jla;++j) for (int k=0;k<=jlb;++k) for (int l=0;l<=jlc;++l) { long long tmp=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],tmp+(1<<i+1)); f[i+1][j+1][k][l+1][0]=min(f[i+1][j+1][k][l+1][0],tmp+(1<<i)); f[i+1][j][k+1][l+1][0]=min(f[i+1][j][k+1][l+1][0],tmp+(1<<i)); f[i+1][j][k][l][0]=min(f[i+1][j][k][l][0],tmp); tmp=f[i][j][k][l][1];//枚举最后一位进位的情况 f[i+1][j+1][k+1][l+1][1]=min(f[i+1][j+1][k+1][l+1][1],tmp+(1<<i+1)); f[i+1][j][k+1][l][1]=min(f[i+1][j][k+1][l][1],tmp+(1<<i)); f[i+1][j+1][k][l][1]=min(f[i+1][j+1][k][l][1],tmp+(1<<i)); f[i+1][j][k][l][0]=min(f[i+1][j][k][l][0],tmp); } } inline void clear()//为做DP初始化 { memset(f,INF,sizeof(f)); n=max((int)log2(a)+1,(int)log2(b)+1); n=max(n,(int)log2(c)+1);//求 n jla=lowbit(a),jlb=lowbit(b),jlc=lowbit(c); } int main() { a=read();b=read();c=read(); clear(); dp(); if(f[n][jla][jlb][jlc][0]>=INF)//注意是“≥INF” { printf("-1\n"); return 0; }//如果无解就输出-1 printf("%lld\n",f[n][jla][jlb][jlc][0]);//输出最小值 return 0; }
一些注意事项:
①本题的 f 数组要开long long 不然会爆。
②INF也要尽量开大。
②在判断无解时要判 " ≥ INF ”(因为转移过程中会加上部分的值)
其他的一些细节瞎搞搞就AC了。