这道题可以加深对于数位DP预处理和分类讨论的理解。
题目:数字游戏2
做法:动态规划
状态表示:
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k] 表示
i
i
i 位数,最高位是
j
j
j ,同时该数各位数字之和除
N
N
N 的余数是
k
k
k.
预处理(状态转移):
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]
=
=
=
∑
x
=
0
9
\sum_{x=0}^9
∑x=09
f
[
i
−
1
]
[
x
]
[
(
k
−
x
)
m
o
d
N
]
f[i-1][x][(k-x)modN]
f[i−1][x][(k−x)modN]
选择集合:先累加在转移即可,具体步骤看代码,latex打太多令人烦躁…
代码及注释:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N=11,M=110;
int a,b,n;
int f[N][10][M];
int mod(int x,int y){
return (x%y+y)%y;
}
void calf(){
memset(f,0,sizeof(f));
for(int i=0;i<=9;i++) f[1][i][mod(i,n)]++; //f[i][j][k]表示i位数字,最高位是j,各位数字和mod N = k 的数字个数
for(int i=2;i<N;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<n;k++)
for(int x=0;x<=9;x++)
f[i][j][k]+=f[i-1][x][mod(k-j,n)];
}
int dp(int x){
if(!x) return 1;
vector<int> num;
while(x) num.push_back(x%10),x/=10;
int res=0;
int last=0;
for(int i=num.size()-1;i>=0;i--){
int u=num[i];
for(int j=0;j<u;j++)
res+=f[i+1][j][mod(-last,n)]; //-last保证为 modN=0,i+1是数字有多少位,要加1因为vector从零开始
last+=u; //转移
if(!i && last%n==0) res++;
}
return res;
}
int main(){
while(cin>>a>>b>>n){
calf();
cout<<dp(b)-dp(a-1)<<endl;
}
return 0;
}
关于这道题,有两点记录:
1、处理每一位数本身时一定一定要注意细节:
a)vector 从后往前处理,要注意位数;
b)不要把选择该位填最大可填数的方案加入,直接转移到 last;
c)最后的一个要注意特判。
2、两点着重思考预处理和集合选择。
P.S. mod 必要时要自己写!