SRM 510 Div1 Easy TheAlmostLuckyNumbersDivOne
問題
lucky numberとは、4,7のことを言う。
almost lucky numberとは、各桁のうちlucky numberでない数が一つ以下の数のことを言う。
a以上b以下のalmost lucky numberの数を求めよ。
制約条件
a,b ≦ 10^16
方針
almost lucky numberは2^14*15*10(300万以下)通り程度。
というわけで素直に全探索して余裕で間に合うのだが、何故か間に合わないと思いこみDPで解いた。
DP解法は以下の通り。
dp[i][j][k][l][m]にi桁目までで、最後の桁がjで、k=1ならleadingzeroが続いていて、l=1なら今までの数はaよりも小さくなってて、non lucky numberがm個含まれているような数の総数。
これを更新していく。
ソースコード
string s; int n; ll dp[20][10][2][2][2]; //pos, last, leadingzero?, smaller?, nonlucky? ll calc(ll x){ stringstream ss; ss<<x; s=ss.str(); n=s.size(); memset(dp,0,sizeof(dp)); dp[0][0][1][0][0]=1; rep(i,n)rep(j,10)rep(lz,2)rep(sm,2)rep(lk,2){ if(sm||j<=s[i]-'0'){ int nlz=lz, nsm=sm, nlk=lk; if(j<s[i]-'0')nsm=1; if(j>0)nlz=0; if(!nlz&&j!=4&&j!=7)nlk++; if(nlk<2)rep(k,10){ dp[i+1][j][nlz][nsm][nlk]+=dp[i][k][lz][sm][lk]; } } } ll ret=0; rep(i,10)rep(sm,2)rep(lk,2)ret+=dp[n][i][0][sm][lk]; return ret; } class TheAlmostLuckyNumbersDivOne { public: long long find(long long a, long long b) { return calc(b)-calc(a-1); } };
追記
dpテーブルに最後の数字を持たせる必要は別になかった。
こんな感じでおk
ll dp[20][2][2][2]; //pos, leadingzero?, smaller?, nonlucky? ll calc(ll x){ stringstream ss; ss<<x; s=ss.str(); n=s.size(); memset(dp,0,sizeof(dp)); dp[0][1][0][0]=1; rep(i,n)rep(j,10)rep(lz,2)rep(sm,2)rep(lk,2){ if(sm||j<=s[i]-'0'){ int nlz=lz, nsm=sm, nlk=lk; if(j<s[i]-'0')nsm=1; if(j>0)nlz=0; if(!nlz&&j!=4&&j!=7)nlk++; if(nlk<2){ dp[i+1][nlz][nsm][nlk]+=dp[i][lz][sm][lk]; } } } ll ret=0; rep(sm,2)rep(lk,2)ret+=dp[n][0][sm][lk]; return ret; }