poj3252 Round Numbers
突然发现好多DP练习没写blog
终于凭借自己的力量AC数位DP了(感动)
考虑维护两个数组,f[i][j][k]表示枚举到第i位,一共有j个0,是否有前导0,d数组在此基础上添加一个性质就是是否到达上界。
那么按定义转移记录答案就行了(好像和记忆化搜索没什么区别?)
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; int len,a[40]; void getnum(int k) { if(k==0){len=1;return ;} len=0; while(k>0) { a[++len]=k%2; k/=2; } } LL f[40][40][2],d[40][40][2];//第几位,几个0,有没有前导零 d是顶上界的 int main() { f[1][0][0]=1;f[1][1][1]=1; for(int i=2;i<=31;i++) for(int j=i;j>=0;j--) f[i][j][0]=f[i-1][j][0]+f[i-1][j][1],//填1 f[i][j][1]=f[i-1][j-1][0]+f[i-1][j-1][1];//填0 int L,R; while(scanf("%d%d",&L,&R)!=EOF) { LL ans=0;L--; getnum(L); memset(d,0,sizeof(d)); d[1][1][1]=1; if(a[1]==1)d[1][0][0]=1; for(int i=2;i<=len;i++) for(int j=0;j<=i;j++) if(a[i]==1) { d[i][j][0]=d[i-1][j][0]+d[i-1][j][1]; if(j>=1)d[i][j][1]=f[i-1][j-1][0]+f[i-1][j-1][1]; } else { if(j>=1)d[i][j][1]=d[i-1][j-1][0]+d[i-1][j-1][1]; } for(int i=1;i<len;i++) for(int j=i/2+i%2;j<=i;j++) ans-=f[i][j][0]; for(int j=len/2+len%2;j<=len;j++) ans-=d[len][j][0]; getnum(R); memset(d,0,sizeof(d)); d[1][1][1]=1; if(a[1]==1)d[1][0][0]=1; for(int i=2;i<=len;i++) for(int j=0;j<=i;j++) if(a[i]==1) { d[i][j][0]=d[i-1][j][0]+d[i-1][j][1]; if(j>=1)d[i][j][1]=f[i-1][j-1][0]+f[i-1][j-1][1]; } else { if(j>=1)d[i][j][1]=d[i-1][j-1][0]+d[i-1][j-1][1]; } for(int i=1;i<len;i++) for(int j=i/2+i%2;j<=i;j++) ans+=f[i][j][0]; for(int j=len/2+len%2;j<=len;j++) ans+=d[len][j][0]; printf("%lld\n",ans); } return 0; }
pain and happy in the cruel world.