求给定区间内不含62和4的数的个数。
数位dp入门。从这里我清楚了一些数位dp的用法。比如limit是判断是否达到上界,而且需要判断(!limit).。比如若题目要求不含11的个数,举例来说:区间在[1,215],当百位开始枚举为0时,十位枚举1,个位可以取0,2~9,即dp[0][1]=9,表示枚举到个位前一位为1时满足的个数,当然此时除了1都满足。而回溯枚举到百位为2,十位为1时,由于dp[0][1]已经枚举了,可以直接返回,但此时返回时有错误的,dp[0][1]=9,而百位为2,十位为1,个位是有限制的。所以记忆化判断是必须要有(!limit),否则会造成重复且答案错误。
所以可以设计dp[pos][sta]表示枚举到第pos为状态为sta时的合法个数。sta指前一位是否为6,所以sta=0或sta=1。
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 int dp[20][2]; 5 int digit[20]; 6 7 int dfs(int pos,int pre,int sta,bool limit) 8 { 9 if(pos==-1) return 1; 10 if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta]; 11 int up=limit?digit[pos]:9; 12 int tmp=0; 13 for(int i=0;i<=up;i++){ 14 if(pre==6&&i==2) continue; 15 if(i==4) continue; 16 tmp+=dfs(pos-1,i,i==6,limit&&i==digit[pos]); 17 } 18 if(!limit) dp[pos][sta]=tmp; 19 return tmp; 20 } 21 22 int solve(int x) 23 { 24 int pos=0; 25 while(x){ 26 digit[pos++]=x%10; 27 x/=10; 28 } 29 return dfs(pos-1,-1,0,true); 30 } 31 32 int main() 33 { 34 int n,m; 35 while(cin>>n>>m,!(n==0&&m==0)) 36 { 37 memset(dp,-1,sizeof(dp)); 38 cout<<solve(m)-solve(n-1)<<endl; 39 } 40 return 0; 41 }
既然sta已包含前一位的信息,所以我觉得dfs时不记录pre也行。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int N=10; 6 int dp[N][2]; 7 int digit[N]; 8 9 int dfs(int pos,int sta,bool limit) 10 { 11 if(pos==0) return 1; 12 if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta]; 13 int up=limit?digit[pos]:9; 14 int ans=0; 15 for(int i=0;i<=up;i++){ 16 if(sta&&i==2) continue; 17 if(i==4) continue; 18 ans+=dfs(pos-1,i==6,limit&&i==digit[pos]); 19 } 20 if(!limit) dp[pos][sta]=ans; 21 return ans; 22 } 23 24 int solve(int x) 25 { 26 int pos=0; 27 while(x) 28 { 29 digit[++pos]=x%10; 30 x/=10; 31 } 32 return dfs(pos,0,true); 33 } 34 35 int main() 36 { 37 int n,m; 38 while(cin>>n>>m,!(n==0&&m==0)) 39 { 40 memset(dp,-1,sizeof(dp)); 41 cout<<solve(m)-solve(n-1)<<endl; 42 } 43 return 0; 44 }
时间: 2024-10-09 05:52:17