AcWing 1081. 度的数量

思路

这是一道数位dp问题,对于数位dp问题关键就在于分类讨论。

首先我们把数字 n 对于B进制来进行分解 ,将每一位上的数字存入一个数组中,然后从高位往低位去讨论,
首先 对于第 i 位数字 x 有三种情况

  1. x = 0 :则 i 位上只能取 0 ,所以直接讨论 i-1 位就可以了
  2. x = 1 :则 i 位上取 0 的时候,后面i-1位都可以随意取值 ,取 1 的时候,后面i- 1位要再小于题目的数的前提下取值,并且能取 k-last-1 个 1 ,
  3. x > 1 :则 i 位上 可以取 1 ,0 ,并且后面 i-1 位可以随便取。

这里 i-1位随便取 和 对于i-1 位讨论的区别,就体现在前一个直接用组合数 f[i-1][k-last]就可以,后面则需要再进入循环去讨论。

最后对于最后一位进行单独讨论,如果对于 最后一位时 ,所有的k个1都已经取好了,也就是k==last了,才做res++。

代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 35;

int K, B;
int f[N][N];

void init(){//求组合数
    for(int i=0;i<=N;i++){
        for(int j=0;j<=i;j++){
            if(!j)f[i][j]=1;
            else f[i][j]=f[i-1][j-1]+f[i-1][j];
        }
    }
}


int dp(int n){
    if (!n) return 0;//如果n==0,那么就直接放回0
    vector<int> v;
    while (n) v.push_back(n % B), n /= B;
    int res=0;
    int last=0;//表示已经取了多少个1
    
    for(int i=v.size()-1;i>=0;i--){//从最高位对每一位数讨论
        int x=v[i];
        if(x){
            res += f[i][K - last];//加上第i位取0的时候的组合数,也就是对于后面i位取k-last个1的数量
            if (x > 1)//如果x>1,就可以直接用组合数表示出来,不用进行讨论,也就是i位取1的时候,后面i位随便取k-last-1个1
            {
                if (K - last - 1 >= 0) res += f[i][K - last - 1];
                break;
            }
            else//如果x==1,那么i位取1的时候,还要进行讨论,后面i位不能随便取,也就不是组合数
            {
                last ++ ;
                if (last > K) break;
            }
        }
       if (!i && last == K) res ++ ;// 对于最后一位来特殊来考虑
    }
    return res;
}

int main()
{
    init();

    int l, r;
    cin >> l >> r >> K >> B;

    cout << dp(r) - dp(l - 1) << endl;

    return 0;
}

发布了175 篇原创文章 · 获赞 1 · 访问量 4258

猜你喜欢

转载自blog.csdn.net/weixin_45080867/article/details/103537349