题目
过程
这是上午考试的第四题。当时自己读完题后,看了数据规模,感觉是故意设计的(本来就是嘛),但是又实在没有方法,就只好打了一个两个dfs嵌套的暴力。。结果(猜也能猜出来):WA×5,TLE×5。
正解:dp。(什么?这破题居然能dp?)
思路:首先预处理出所有的情况,然后O(1)输出。
dp数组开成[32][32][32][32][2]大小,f[l][i][j][k][jin]表示Z的长度最大为l、X
a(2)
中1的数量为i、Y
a(2)
中1的数量为j、Z
a(2)
中1的数量为k、进位为jin的最小的符合条件的答案(没有为-1)。
枚举l、i、j、k、jin以后,如果情况成立,我们用p、q、r分别枚举X、Y、Z的下一位,若情况仍成立,那么我们就可以进行状态转移。状态转移方程如下:
if(f[l+1][i+p][j+q][k+r][(p+q+jin-r)/2]==-1||f[l][i][j][k][jin]+r*(1<<l)<f[l+1][i+p][j+q][k+r][(p+q+jin-r)/2])
f[l+1][i+p][j+q][k+r][(p+q+jin-r)/2]=f[l][i][j][k][jin]+r*(1<<l);
其中,l+1为新生成的Z的长度,i+p、j+q、k+r不必说,(p+q+jin-r)/2为新产生的Z的进位。为什么呢?因为p+q+jin更新了Z,r为新生成的Z的个位,减去r再整除2即为新的Z在二进制下的余数。
其余的请见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,a,b,c,g1,g2,g3,l1,l2,l3,i,len;
int f[32][32][32][32][2];
void init(int n,int &length,int &ge)
{
length=ge=0;
while(n)
{
if(n%2) ge++;
n>>=1;
length++;
}
}
void dp()
{
memset(f,-1,sizeof(f));
f[0][0][0][0][0]=0;
for(int l=0;l<=30;l++)
for(int i=0;i<=30;i++)
for(int j=0;j<=30;j++)
for(int k=0;k<=30;k++)
for(int jin=0;jin<=1;jin++)
if(f[l][i][j][k][jin]>=0)
for(int p=0;p<=1;p++)
for(int q=0;q<=1;q++)
for(int r=0;r<=1;r++)
if((p+q+jin)%2==r)
if(f[l+1][i+p][j+q][k+r][(p+q+jin-r)/2]==-1||f[l][i][j][k][jin]+r*(1<<l)<f[l+1][i+p][j+q][k+r][(p+q+jin-r)/2])
f[l+1][i+p][j+q][k+r][(p+q+jin-r)/2]=f[l][i][j][k][jin]+r*(1<<l);
}
main()
{
freopen("binary.in","r",stdin);
freopen("binary.out","w",stdout);
dp();
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d%d",&a,&b,&c);
init(a,l1,g1);
init(b,l2,g2);
init(c,l3,g3);
len=max(l1,max(l2,l3));
printf("%d\n",f[len][g1][g2][g3][0]);
}
}