BZOJ3107 CQOI2013二进制A+B(动态规划)
显然答案只与a、b、c中各自1的个数及位数有关。a、b只考虑前i位怎么填时,c最多在第i+1位上为1,而第i+1位及之后的a、b怎么填都不会对前i位造成影响。于是设f[n][i][j][k][0/1]表示只考虑前n位,a用i个1,b用j个1,c用k个1,且c的第n+1位为0/1时的最小值。转移时枚举下一位a和b各自填0还是1即可。注意230是有31位的,防止爆int。本来输出的时候是三目运算符的结果发现-1会输出成232-1。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define N 32 #define int unsigned int #define inf ((1<<31)-1) int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int a,b,c,n,f[N][N][N][N][2]; inline void update(int &x,int y){x=min(x,y);} signed main() { #ifndef ONLINE_JUDGE freopen("bzoj3107.in","r",stdin); freopen("bzoj3107.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif a=read(),b=read(),c=read(); int t=0,cnt=0; while (a) t++,cnt+=a&1,a>>=1; n=max(n,t);a=cnt; t=0,cnt=0; while (b) t++,cnt+=b&1,b>>=1; n=max(n,t);b=cnt; t=0,cnt=0; while (c) t++,cnt+=c&1,c>>=1; n=max(n,t);c=cnt; for (int i=0;i<=n;i++) for (int j=0;j<=a;j++) for (int k=0;k<=b;k++) for (int l=0;l<=c;l++) f[i][j][k][l][0]=f[i][j][k][l][1]=inf; f[0][0][0][0][0]=0; for (int i=0;i<n;i++) for (int j=0;j<=min(a,i);j++) for (int k=0;k<=min(b,i);k++) for (int l=0;l<=min(c,i+1);l++) { update(f[i+1][j][k][l][0],min(f[i][j][k][l][0],f[i][j][k][l][1])); update(f[i+1][j+1][k][l+1][0],f[i][j][k][l][0]+(1<<i)); update(f[i+1][j][k+1][l+1][0],f[i][j][k][l][0]+(1<<i)); update(f[i+1][j+1][k][l][1],f[i][j][k][l][1]+(1<<i)); update(f[i+1][j][k+1][l][1],f[i][j][k][l][1]+(1<<i)); if (i+1<n) update(f[i+1][j+1][k+1][l+1][1],min(f[i][j][k][l][0],f[i][j][k][l][1])+(1<<i+1)); } if (f[n][a][b][c][0]==inf) cout<<-1; else cout<<f[n][a][b][c][0]; return 0; }