【学习笔记&训练记录】数位DP
数位DP,即对数位进行拆分,利用数位来转移的一种DP,一般采用记忆化搜索,或者是先预处理再进行转移
一个比较大略的思想就是可以对于给定的大数,进行按数位进行固定来转移记录答案
区间类型的,可以考虑前缀和的思想,求[l,r]可以看做求[1,r]-[1,l)
其实还有一种,是按照二进制建一颗0,1树来表示,来做,但是比并没有做过,以后再总结
HDU-2089
题目大意:对于区间[L,R]求有多少不包含'62'且不包含'4'的数,题目允许有前导零
思路:
数位DP,考虑F[i][j]表示位数为i,最高位为j的满足的个数
预处理后,统计答案即可,统计答案大致就是固定每一位,进行统计
CODE:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m; int F[10][10]; void prework() { F[0][0]=1; for (int i=1; i<=7; i++) for (int j=0; j<=9; j++) for (int k=0; k<=9; k++) if (j!=4 && !(j==6&&k==2)) F[i][j]+=F[i-1][k]; } int Calc(int x) { int digit[10]={0},len=0,ans=0; while (x!=0) {digit[++len]=x%10; x/=10;} for (int i=len; i>=1; i--) { for (int j=0; j<=digit[i]-1; j++) if (j!=4 && !(j==2&&digit[i+1]==6)) ans+=F[i][j]; if (digit[i]==4 || (digit[i]==2&&digit[i+1]==6)) break; } return ans; } int main() { prework(); n=read(),m=read(); if (n>m) swap(n,m); while (n!=0 && m!=0) { printf("%d\n",Calc(m+1)-Calc(n)); n=read(),m=read(); if (n>m) swap(n,m); } return 0; }
HDU-3652
题目大意:给定n,求到n中,包含'13'且被13整除的数的个数
思路:
设计状态F[i][j][k][0/1]表示位数为i,最高位为j的%13余k的数字包含和不包含13的个数
那么同样预处理,这里不含前导零,需要额外做一个值去进行计算,计算到n以内的,计算n+1即可
CODE:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; long long n; long long F[20][20][15][2]; long long cf(int x,int y) { long long re=x; for (int i=0; i<y; i++) re*=10; return re; } void prework() { for (int i=0; i<=9; i++) F[1][i][i%13][1]=1; for (int i=2; i<=10; i++) for (int tmp,j=0; j<=9; j++) { tmp=cf(j,i-1); for (int k=0; k<=9; k++) for (int l=0; l<13; l++) { F[i][j][(tmp+l)%13][0]+=F[i-1][k][l][0]; if (j==1 && k==3) F[i][j][(tmp+l)%13][0]+=F[i-1][k][l][1]; else F[i][j][(tmp+l)%13][1]+=F[i-1][k][l][1]; } } // for (int i=1; i<=10; i++) // for (int j=0; j<=9; j++) // for (int k=0; k<13; k++) // printf("%I64d %I64d\n",F[i][j][k][1],F[i][j][k][0]); } long long Calc(long long x) { int digit[15]={0},len=0,f=0; long long ans=0; while (x) {digit[++len]=x%10; x/=10;} for (int i=0; i<=digit[len]-1; i++) ans+=F[len][i][0][0]; long long tmp=cf(digit[len],len-1); for (int tt,i=len-1; i>=1; i--) { for (int j=0; j<=digit[i]-1; j++) for (int k=0; k<13; k++) { if ((tmp+k)%13==0) ans+=F[i][j][k][0]; if ((tmp+k)%13==0 && (digit[i+1]==1 && j==3)) ans+=F[i][j][k][1]; else if ((tmp+k)%13==0 && f) ans+=F[i][j][k][1]; } if (digit[i]==3 && digit[i+1]==1) f=1; tmp+=cf(digit[i],i-1); } return ans; } int main() { prework(); while (scanf("%lld",&n)!=EOF) printf("%lld\n",Calc(n+1)); return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.