数位DP

模板题:1082. 数字游戏

题目描述
求整数区间 [L,R][L,R] 内, 不降数 的个数

不降数:数位从高到低呈非下降关系(【例】:123,446)

image
image

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
#define N 15
int f[N][N];//f[i][j]表示i位数且最高位(最左端为最高位)为j的不降序的方案数
void init(){
   for(int i=0;i<=9;i++) f[1][i]=1;

   for(int i=2;i<N;i++)
       for(int j=0;j<=9;j++)
           for(int k=j;k<=9;k++){
              f[i][j]+=f[i-1][k];
            //   cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
           } 
}
int dp(int n){
    if(n==0) return 1;
    vector<int> nums;
    while(n) nums.push_back(n%10),n/=10;
    // cout<<n<<endl;
    // cout<<nums[0]<<endl;
    int ans=0;
    int last=0;
    for (int i=nums.size()-1; i>=0; i--){
        int x=nums[i];
        for(int j=last;j<x;j++)
           ans+=f[i+1][j];
        if(last>x) break;
        last=x;
         if(!i) ans++;//全部枚举到最低位的右分支。
    }
    return ans;
}

signed main()
{
    init();
    int n,m;
     while(cin>>n>>m)
    //  cout<<n<<m<<" "<<dp(n-1)<<endl;
      printf("%d\n",dp(m)-dp(n-1));
    return 0;
}

1083. Windy数

思路:
数位Dp
假设我们当前枚举到第i位(设共有n位),且第i位上的数字为x,那么对于答案中第i位数字j来说,有两类:

1.0~x-1 (如果第i位是最高位,这里是1~x-1)
括号中提到的1~x-1后面会解释 ,我们用last记录上一位的数字,然后枚举j,如果abs(j-last) >= 2 就累加答案,res += f[i+1][j];

2.x
不需要枚举j,last = x,再枚举之后的数位即可

上述做完之后,由于上面的答案都是n位的,对于数位个数低于n的,再累加到答案中就行了

f数组的处理
f[i][j] 表示一共有i位,且最高位数字为j的满足windy数定义的数的个数

状态转移: 因为第i位是j已经确定,考虑第i-1位,设第i-1位数字为k,那么根据windy数定义只要abs(k-j) >= 2就可以转移过来

\(f[i][j]=∑^9_{k=0}if(abs(k−j)>=2)f[i−1][k]\)

关于前导0
上面提到了枚举的第i位是最高位,那么不能填0,这里解释一下,如果我们填0,那么答案就会加上f[i+1][0],,举这样一个例子,
对于数字13,他是满足windy数定义的,那么加上前导0之后的013就不会被f[3][0]加进去,原因就是abs(0-1)<2,这样就导致答案漏掉。

作者:Moon_light
链接:https://www.acwing.com/solution/content/15562/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int N=11;
int f[N][N];
 //f[i][j] 表示一共有i位,
 //且最高(左边为高位)为是数字j的满足wingy数定义的数的个数
void init(){
    for(int i=0;i<=9;i++) f[1][i]=1;
    for(int i=2;i<=N;i++)
        for(int j=0;j<=9;j++)
            for(int k=0;k<=9;k++)
                if(abs(k-j)>=2) f[i][j]+=f[i-1][k];
            
}
int dp(int n){
    int ans=0;
    int last=-2;
    vector<int>  vec;
    while(n) vec.push_back(n%10) ,n/=10;
    int len=vec.size()-1;
    //答案是len位的情况
    for(int i=len;i>=0;i--){
        int x=vec[i];
        for(int j=(i==len);j<x;j++){//最高位从1开始
            if(abs(j-last)>=2) ans+=f[i+1][j];
        }
        if(abs(x-last)<2) break;
            last=x;
        if(!i) ans++;
    }
    //答案小于a.size()位的
    for(int i = 1; i<=len; i++)
        for(int j = 1; j<=9; j++)
            ans += f[i][j];
    return ans;
}
signed main(){
    init();
    int n,m;
    cin>>n>>m;
    cout<<dp(m)-dp(n-1)<<endl;
    return  0;
}
posted @ 2022-09-23 21:21  kingwzun  阅读(30)  评论(0)    收藏  举报