[BZOJ1026][SCOI2009]windy数(数位dp)

题目描述

传送门

题解

状态:f(i,j)表示i位数,第i位是j,每位相差至少为2的数的个数
转移: if (Abs(j,k)>=2) f[i][j]+=f[i-1][k];
注意:这道题处理前导0的情况比较吃屎,这里的转移方程是不包括前导0的情况,我的处理方法是在求解的时候单独算。
细节也比较多。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int n,m,f[20][20],g[20][20],digit[20];
inline int Abs(int a,int b){return (a>b)?a-b:b-a;}
inline void dp(){
    for (int i=0;i<10;++i) f[1][i]=1;
    for (int i=0;i<10;++i) g[1][i]=1;
    for (int i=2;i<=10;++i)
      for (int j=0;j<10;++j)
        for (int k=0;k<10;++k)
          if (Abs(j,k)>=2)
            f[i][j]+=f[i-1][k];
}
inline int calc_ans(int n){
    int cnt=0,ans=0;memset(digit,0,sizeof(digit));
    while (n) digit[++cnt]=n%10,n/=10;

    for (int i=1;i<cnt;++i)
      for (int j=1;j<10;++j) 
        ans+=f[i][j];

    for (int i=cnt;i>=1;--i){
        for (int j=0;j<digit[i];++j)
          if (i==cnt&&!j) continue;
          else if (i==cnt||Abs(j,digit[i+1])>=2) ans+=f[i][j];

        if (i!=cnt&&Abs(digit[i],digit[i+1])<2) break;
    }
    return ans;
}

int main(){
    dp();
    scanf("%d%d",&n,&m);
    printf("%d\n",calc_ans(m+1)-calc_ans(n));
}

你可能感兴趣的:(dp,bzoj,SCOI)