数位dp 组合数 usaco training 3.2 1382. 比特串 - AcWing题库 给定一个由若干个 位 01 串构成的集合

这个集合内的元素满足:

  • 按照二进制表示从小到大排序。
  • 每一个元素的 位字符中,字符 的数量均不超过
  • 所有满足上一条件的 串保证都在集合之中。

现在请你求出这个集合中的第 个元素是多少。

输入格式

共一行,包含三个整数

输出格式

输出集合中的第 个元素。

数据范围

,
,
中元素的个数

输入样例:

5 3 19

输出样例:

10011

思路

直接考虑枚举答案, 既然要求第I个数, 只有当前位选1的方案数等于I时, 将当前位输出(即选0时方案数小于I), 否则输出0, 这样可以贪心地求出最小。

设DP数组: f[i][j] 长度为i, 当前最多可以用j个1

先求出全排列数组, 之后预处理f数组。预处理时, j枚举应当超出i的限制, 最多为n, 因为状态定义的是最多可以用。第三层循环枚举当前选择的1个数k, 其大于i也没事, 全排列数组中对于这样的会初始化为0。

DP求解时从高到低位枚举, 若当前选0时方案数小于I, 说明当前需要选1, 即 f[i-1][L] < I 时, cout << 1, 然后再将 I-=f[i-1][L], 因为当前选1之后, 下一位能选的方案数就不能再是I了, 还要把当前能用的1个数减1, L--

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 32;
typedef long long LL;
LL f[N][N], c[N][N];
 
int main()
{
    LL n,l,m;
    cin >> n >> m >> l;
    
    c[0][0] = 1;
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= i; j++)
            if(!j) c[i][j] = 1;
            else c[i][j] = c[i- 1][j] + c[i-1][j-1];
    
    for(int i = 0; i <= n; i++) // 第i位
        for(int j = 0; j <= n; j++) // 当前位后面最多选j个1
            for(int k = 0; k <= j; k++) // 选1
                f[i][j] += c[i][k];
    
    for(int i = n; i >= 1; i--)
    {
        if(f[i-1][m] < l)
        {
            cout << 1;
            l -= f[i-1][m];
            m--;
        }else cout << 0;
        
    }
                
    
    return 0;
}