标签:
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 25802 Accepted Submission(s): 8967
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> using namespace std ; int dp[15][3]; //dp[len][0]代表不含4或62的个数 //dp[len][1]代表不含4或62的个数,首位为2 //dp[len][2]代表含4或62的个数 void init() { memset(dp,0,sizeof(dp)); //dp[1][0]=9; //dp[1][1]=1; //dp[1][2]=1; dp[0][0]=1; for(int len=1;len<=9;len++) { dp[len][0]=dp[len-1][0]*9-dp[len-1][1];////在最高位加上除了4之外的9个数字,但是可能在2之前加了6 dp[len][1]=dp[len-1][0]; //就是在原先不含4或62的最高位加2 dp[len][2]=10*dp[len-1][2]+dp[len-1][1]+dp[len-1][0];
//在已经有不吉利数字最高位加任意数字,或者在无吉利数字前加4,或者在2前面加4 } } int solve(int x) { int s[10],len=0,xx=x; while(x>0) { s[++len]=x%10; x/=10; } s[len+1]=0; //初始化前缀为0,0是没有任何影响的,后面一位可能会用到前面一位 // cout<<len<<endl; int ans=0,flag=0; for(int i=len;i>=1;i--) { ans+=(s[i]*dp[i-1][2]); if(flag) //如果前缀中有出现4或者62的话,那么后面的数就全加上 { ans+=s[i]*dp[i-1][0]; } //只考虑以len位置为i的开头的数 if(!flag && s[i]>4) ans+=dp[i-1][0]; if(!flag && s[i]>6) ans+=dp[i-1][1];//因为是大于号,所以低一位可以完全枚举 //考虑前缀的影响 if(!flag && s[i+1]==6 && s[i]>2) ans+=dp[i][1]; if(s[i]==4 || (s[i+1]==6 && s[i]==2) ) { flag=1; } } return xx-ans; } int main() { init() ; int n,m ; // while(scanf("%d%d",&n,&m)) { if(n==0 && m==0) break ; // solve(101); printf("%d\n",solve(m+1)-solve(n)) ;//用[0,m]-[0,n)即可得到区间[n,m] } return 0 ; }
dp[len][0]代表首位不为6,剩余长度为len,不含4和62的数的个数,
dp[len][1]代表首位为6,剩余长度为len,不含4和62的数的个数。
同样是枚举每个位置上的数,但是不同的是对于枚举当当前位置最大值的处理,dfs通过fp(代表是否是n的前缀)
这个参数只是用来告诉下一个状态是枚举到9,还是枚举到自身位置的最大值,如果是前缀就只能枚举当自身位置的最大值,
不是就可以从0枚举到9.还有一个参数是用来前缀的最后一位是否为6的状态,
不为6,ret+=dp[len-1][0],
为6,ret+=dp[len][1];
#include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> using namespace std; #define LL long long LL dp[30][2]; int digit[30]; LL dfs(int len,bool state,bool fp) { if(!len) return 1; if(!fp && dp[len][state] != -1) //如果不是前缀,并且已经被计算过 return dp[len][state]; //是前缀的话,说明dp[len][state]不能用,就得重新搜索(枚举)。 LL ret = 0 ;int fpmax = fp ? digit[len] : 9; for(int i=0;i<=fpmax;i++) //分别算出以i开头的数的方案数, //62,062,0062,因为位数是固定的,假设是m位,那么都不足m位,后面肯定有东西 { if(i==4 || state && i == 2) continue; //i如果不是6的话,ret+=dp[len][0],如果是6的话,ret+=dp[len][1] ret += dfs(len-1,i == 6,fp && i == fpmax); //第二个参数来保存前缀的最后一位是否为6的状态,如果为6,下一位就不能为2,否则没有限制 //第三个参数表明是否是前缀,如果是前缀,下一位就只能枚举到最大值,否则就没有限制 } if(!fp) //如果是前缀,当前得到的ret的值,就不能代表dp[len][state]的值 dp[len][state] = ret; return ret; //ret代表0到n不含4和62的个数 } LL f(LL n) { int len = 0; while(n) { digit[++len] = n % 10; n /= 10; } return dfs(len,false,true); } int main() { //freopen("test.txt","r",stdin); LL a,b; memset(dp,-1,sizeof(dp)); while(cin>>a>>b) { if(a==0 && b==0) break; cout<<f(b)-f(a-1)<<endl; } return 0; }
比较这两种方式的异同点,递推是利用具有相同前缀的数含4或62的方案数,跟去掉前缀之后的方案数相等这一特性,
而dfs是利用记录是否为前缀这一状态,搜索时,来判断每一位枚举的范围.记忆化搜索得到答案.
标签:
原文地址:http://www.cnblogs.com/xianbin7/p/4718811.html