数位DP总结
数位DP=区间求符合条件的数的个数的算法=数很大,无法直接暴力的算法≈让人苦恼愤怒,依然D不出来的勾魂算法
数位DP的两种写法:
1、递推
结构:
init()函数,根据递推数位的公式求得的满足某长度的符合数的个数
cal()函数,假设要计算的数字为n,数位长度为len。1到len-1长度累加,len长度的拿出来一个位置一个位置遍历判定再累加。
输出cal(right+1)-cal(left),right为区间最右,left为区间最左
模板:
int f[35][35],digit[35]; void Init() { int i,j; /*递推关系式,请忽略 f[0][0]=1; for(i=1; i<32; ++i) { f[i][0]=f[i-1][0]; for(j=1; j<=i; ++j) f[i][j]=f[i-1][j]+f[i-1][j-1]; } */ } int Cal(int x,int k,int b) { int tot=0,len=0,ans=0,i; while(x) //初始化数位 { digit[++len]=x%b; x/=b; } for(i=len;i&&tot<=k;--i) //分长度累加 { /*判断累加,请忽略 if(digit[i]>1) { ans+=f[i][k-tot];break; } if(digit[i]==1) { ans+=f[i-1][k-tot]; tot++; } */ } return ans; } 输出Cal(right+1)-Cal(left)
2、递归(记忆化搜索,据说记忆化搜索最优)
个人觉得记忆化搜索更简单,因为它的结构简单。dp数组d的是pos前面数组的状态。
结构:
dfs()函数,记忆化搜索符合条件的数的个数。记忆化搜索的参数无非是表示上一状态或者是下一状态的。
cal()函数,初始化数位,调用dfs函数。
输出cal(right)-cal(left-1)
模板:
int dfs(int pos , int pre , int have , bool doing) //doing为边界,pos为计算到当前第几位 { if(pos == -1) return have == 2 && pre == 0; if(!doing && dp[pos][pre][have] != -1) //若不是统计边界位,则直接返回记忆下的数 return dp[pos][pre][have]; int ans = 0; int end = doing ? digit[pos] : 9; for(int i = 0 ; i <= end ; i ++) { /*初始化下一层dfs的参数,请忽略 int npre = (pre*10 + i) % 13; int nhave = have; if(have == 0 && i == 1) nhave = 1; else if(have == 1 && i != 1) nhave = 0; if(have == 1 && i == 3) nhave = 2; */ ans += dfs(pos-1 , npre , nhave , doing && i == end ); //doing在i==end的时候会是1 } if(!doing) dp[pos][pre][have] = ans; return ans; } int cal(int x) { int pos = 0; while(x) { digit[pos++] = x % 10; x /= 10; } return dfs(pos - 1 , 0 , 0 , 1); }
输出Cal(right)-Cal(left-1)
                    
                
                
            
        
浙公网安备 33010602011771号