C++数位DP

数位DP,对应蓝桥云课 二进制问题 代码框架见下

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define ll long long
#define maxd 65

//一般情况参数下,1个参数居多,部分情况会有两个参数
//当需要三个参数的时候,就需要修改结构体了
struct DpData {
    ll data0;
    ll data1;
    ll data2;
    static ll K;
    static ll base;
    DpData() : data0(0), data1(0) {
        init();
    }
    void init();
    ll dfsReturn(bool is_leadingZero)const;
    DpData getNextDpData(bool is_leadingZero, int digit) const;
};
#define data0_max maxd
#define data1_max 2
ll dp[maxd][2][2][data0_max][data1_max];
// 1 修改点,通过输入数据进行输入
ll DpData::K = 0;
// 2 修改点,通过题目条件进行修改,二进制就是2,十进制就是10, 也有可能通过数据输入进行输入
ll DpData::base = 2;
// 3 修改点,数据的初始化,确定data0和data1表示的是什么
void DpData::init() {
    data0 = 0;//代表二进制中1的个数
    data1 = 0;//留空不用
}
// 4 修改点 dfs返回值
ll DpData::dfsReturn(bool is_leadingZero)const {
    if (is_leadingZero) {
        return K == 0;
    }
    return data0 == K;
}

// 5 修改点,状态转移的过程
DpData DpData::getNextDpData(bool is_leadingZero, int digit) const {
    DpData ret = *this;
    if (is_leadingZero) {

    }
    else {

    }
    if (digit == 1) {
        ret.data0++;
    }
    return ret;
}

ll dfs(
    const string& num, //数字字符串
    int depth,         //当前枚举到的是第几个数位
    bool is_leadingZero,//默认为true,代表前面枚举的都是0
    bool is_limit, //默认为false,代表前面的所有位都和num相等
    DpData dpdata //数位DP用到的核心数据结构
) {
    if (depth == num.size()) {
        return dpdata.dfsReturn(is_leadingZero);
    }
    int maxdigit = is_limit ? (DpData::base - 1) : (num[depth] - '0');
    ll& ans = dp[depth][is_leadingZero][is_limit][dpdata.data0][dpdata.data1];
    if (ans != -1) {
        return ans;
    }
    ans = 0;
    for (int i = 0; i <= maxdigit; ++i) {
        ans += dfs(
            num,
            depth + 1,
            is_leadingZero && (i == 0),
            is_limit || (i < maxdigit),
            dpdata.getNextDpData(is_leadingZero, i)
        );
    }
    return ans;
}

//固定模版,不需要修改,求[0, n]中所有满足条件的数的数量
ll getans(ll n) {
    memset(dp, -1, sizeof(dp));
    int a[maxd], asize = 0;
    string s;
    while (n) {
        a[asize++] = n % DpData::base;
        n /= DpData::base;
    }
    if (asize == 0) {
        a[asize++] = 0;
    }
    for (int i = asize - 1; i >= 0; --i) {
        s.push_back('0' + a[i]);
    }
    DpData dpd;
    return dfs(s, 0, true, false, dpd);
}

//固定模版,数位DP的差分操作,求[l, r]中所有满足条件的数的个数
ll getans(ll l, ll r) {
    return getans(r) - getans(l - 1);

}
int main()
{
    ll r;
    cin >> r >> DpData::K;
    cout << getans(1, r) << endl;

    // 请在此输入您的代码
    return 0;
}

代码练习 1 长官和他的猫 蓝桥云课 代码见下

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define ll long long
#define maxd 65

//一般情况参数下,1个参数居多,部分情况会有两个参数
//当需要三个参数的时候,就需要修改结构体了
struct DpData {
    ll data0;
    ll data1;
    ll data2;
    static ll K;
    static ll base;
    DpData() : data0(0), data1(0) {
        init();
    }
    void init();
    ll dfsReturn(bool is_leadingZero)const;
    DpData getNextDpData(bool is_leadingZero, int digit) const;
};
#define data0_max maxd
#define data1_max 2
ll dp[maxd][2][2][data0_max][data1_max];
// 1 修改点,通过输入数据进行输入
ll DpData::K = 0;
// 2 修改点,通过题目条件进行修改,二进制就是2,十进制就是10, 也有可能通过数据输入进行输入
ll DpData::base = 10;
// 3 修改点,数据的初始化,确定data0和data1表示的是什么
void DpData::init() {
    data0 = 0;//上一位是什么
    data1 = 1;//是否满足给定的条件
}
// 4 修改点 dfs返回值
ll DpData::dfsReturn(bool is_leadingZero)const {
    if (is_leadingZero) {
        return 1;
    }
    return data1;
}

// 5 修改点,状态转移的过程
DpData DpData::getNextDpData(bool is_leadingZero, int digit) const {
    DpData ret = *this;
    if (is_leadingZero) {

    }
    else {
      if(abs(digit - ret.data0) > DpData::K){
        ret.data1 = 0;
      }

    }
    ret.data0 = digit;
    return ret;
}

ll dfs(
    const string& num, //数字字符串
    int depth,         //当前枚举到的是第几个数位
    bool is_leadingZero,//默认为true,代表前面枚举的都是0
    bool is_limit, //默认为false,代表前面的所有位都和num相等
    DpData dpdata //数位DP用到的核心数据结构
) {
    if (depth == num.size()) {
        return dpdata.dfsReturn(is_leadingZero);
    }
    int maxdigit = is_limit ? (DpData::base - 1) : (num[depth] - '0');
    ll& ans = dp[depth][is_leadingZero][is_limit][dpdata.data0][dpdata.data1];
    if (ans != -1) {
        return ans;
    }
    ans = 0;
    for (int i = 0; i <= maxdigit; ++i) {
        ans += dfs(
            num,
            depth + 1,
            is_leadingZero && (i == 0),
            is_limit || (i < maxdigit),
            dpdata.getNextDpData(is_leadingZero, i)
        );
    }
    return ans;
}

//固定模版,不需要修改,求[0, n]中所有满足条件的数的数量
ll getans(ll n) {
    memset(dp, -1, sizeof(dp));
    int a[maxd], asize = 0;
    string s;
    while (n) {
        a[asize++] = n % DpData::base;
        n /= DpData::base;
    }
    if (asize == 0) {
        a[asize++] = 0;
    }
    for (int i = asize - 1; i >= 0; --i) {
        s.push_back('0' + a[i]);
    }
    DpData dpd;
    return dfs(s, 0, true, false, dpd);
}

//固定模版,数位DP的差分操作,求[l, r]中所有满足条件的数的个数
ll getans(ll l, ll r) {
    return getans(r) - getans(l - 1);

}
int main()
{
  ll l, r;
  cin >> l >> r;
  cin >> DpData::K;
  cout << getans(l, r) << endl;
    // 请在此输入您的代码
    return 0;
}

代码练习2 幸运年 对应蓝桥云课 代码见下

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define ll long long
#define maxd 65

//一般情况参数下,1个参数居多,部分情况会有两个参数
//当需要三个参数的时候,就需要修改结构体了
struct DpData {
    ll data0;
    ll data1;
    ll data2;
    static ll K;
    static ll base;
    DpData() : data0(0), data1(0) {
        init();
    }
    void init();
    ll dfsReturn(bool is_leadingZero)const;
    DpData getNextDpData(bool is_leadingZero, int digit) const;
};
#define data0_max 1000
#define data1_max 2
ll dp[maxd][2][2][data0_max][data1_max];
// 1 修改点,通过输入数据进行输入
ll DpData::K = 0;
// 2 修改点,通过题目条件进行修改,二进制就是2,十进制就是10, 也有可能通过数据输入进行输入
ll DpData::base = 10;
// 3 修改点,数据的初始化,确定data0和data1表示的是什么
void DpData::init() {
    data0 = 0;//最后一个数位模上1000的值
    data1 = 0;//这三个数是否满足条件
}
// 4 修改点 dfs返回值
ll DpData::dfsReturn(bool is_leadingZero)const {
    if (is_leadingZero) {
        return 0;
    }
    return data1;
}

// 5 修改点,状态转移的过程
DpData DpData::getNextDpData(bool is_leadingZero, int digit) const {
    DpData ret = *this;
    if (is_leadingZero) {

    }
    else {

    }
    if (ret.data0 == 202 && digit == 3) {
        ret.data1 = 1;
    }
    if(ret.data0 % 10 == 1 && digit == 4){
        ret.data1 = 1;
    }
    ret.data0 = (ret.data0 * 10 + digit) % 1000;
    return ret;
}

ll dfs(
    const string& num, //数字字符串
    int depth,         //当前枚举到的是第几个数位
    bool is_leadingZero,//默认为true,代表前面枚举的都是0
    bool is_limit, //默认为false,代表前面的所有位都和num相等
    DpData dpdata //数位DP用到的核心数据结构
) {
    if (depth == num.size()) {
        return dpdata.dfsReturn(is_leadingZero);
    }
    int maxdigit = is_limit ? (DpData::base - 1) : (num[depth] - '0');
    ll& ans = dp[depth][is_leadingZero][is_limit][dpdata.data0][dpdata.data1];
    if (ans != -1) {
        return ans;
    }
    ans = 0;
    for (int i = 0; i <= maxdigit; ++i) {
        ans += dfs(
            num,
            depth + 1,
            is_leadingZero && (i == 0),
            is_limit || (i < maxdigit),
            dpdata.getNextDpData(is_leadingZero, i)
        );
    }
    return ans;
}

//固定模版,不需要修改,求[0, n]中所有满足条件的数的数量
ll getans(ll n) {
    memset(dp, -1, sizeof(dp));
    int a[maxd], asize = 0;
    string s;
    while (n) {
        a[asize++] = n % DpData::base;
        n /= DpData::base;
    }
    if (asize == 0) {
        a[asize++] = 0;
    }
    for (int i = asize - 1; i >= 0; --i) {
        s.push_back('0' + a[i]);
    }
    DpData dpd;
    return dfs(s, 0, true, false, dpd);
}

//固定模版,数位DP的差分操作,求[l, r]中所有满足条件的数的个数
ll getans(ll l, ll r) {
    return getans(r) - getans(l - 1);

}
int main()
{
    ll l, r;
    cin >> l >> r;
    cout << getans(l, r) << endl;
    return 0;
}

代码练习3 对应蓝桥云课 数数问题 代码见下

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define ll long long
#define maxd 65

//一般情况参数下,1个参数居多,部分情况会有两个参数
//当需要三个参数的时候,就需要修改结构体了
struct DpData {
    ll data0;
    ll data1;
    ll data2;
    static ll K;
    static ll base;
    DpData() : data0(0), data1(0) {
        init();
    }
    void init();
    ll dfsReturn(bool is_leadingZero)const;
    DpData getNextDpData(bool is_leadingZero, int digit) const;
};
#define data0_max 305
#define data1_max 2
ll dp[maxd][2][2][data0_max][data1_max];
// 1 修改点,通过输入数据进行输入
ll DpData::K = 0;
// 2 修改点,通过题目条件进行修改,二进制就是2,十进制就是10, 也有可能通过数据输入进行输入
ll DpData::base = 2;
// 3 修改点,数据的初始化,确定data0和data1表示的是什么
void DpData::init() {
    data0 = 0;//代表所有数字之和
    data1 = 0;//留空不用
}
// 4 修改点 dfs返回值
ll DpData::dfsReturn(bool is_leadingZero)const {
    if (is_leadingZero) {
        return K == 0 ? 1 : 0;
    }
    return data0 == K;
}

// 5 修改点,状态转移的过程
DpData DpData::getNextDpData(bool is_leadingZero, int digit) const {
    DpData ret = *this;
    if (is_leadingZero) {
      ret.data0 = digit;
    }
    else {
      ret.data0 += digit;
    }
    if (digit == 1) {
        ret.data0++;
    }
    return ret;
}

ll dfs(
    const string& num, //数字字符串
    int depth,         //当前枚举到的是第几个数位
    bool is_leadingZero,//默认为true,代表前面枚举的都是0
    bool is_limit, //默认为false,代表前面的所有位都和num相等
    DpData dpdata //数位DP用到的核心数据结构
) {
    if (depth == num.size()) {
        return dpdata.dfsReturn(is_leadingZero);
    }
    int maxdigit = is_limit ? (DpData::base - 1) : (num[depth] - '0');
    ll& ans = dp[depth][is_leadingZero][is_limit][dpdata.data0][dpdata.data1];
    if (ans != -1) {
        return ans;
    }
    ans = 0;
    for (int i = 0; i <= maxdigit; ++i) {
        ans += dfs(
            num,
            depth + 1,
            is_leadingZero && (i == 0),
            is_limit || (i < maxdigit),
            dpdata.getNextDpData(is_leadingZero, i)
        );
    }
    return ans;
}

//固定模版,不需要修改,求[0, n]中所有满足条件的数的数量
ll getans(ll n) {
    memset(dp, -1, sizeof(dp));
    int a[maxd], asize = 0;
    string s;
    while (n) {
        a[asize++] = n % DpData::base;
        n /= DpData::base;
    }
    if (asize == 0) {
        a[asize++] = 0;
    }
    for (int i = asize - 1; i >= 0; --i) {
        s.push_back('0' + a[i]);
    }
    DpData dpd;
    return dfs(s, 0, true, false, dpd);
}

//固定模版,数位DP的差分操作,求[l, r]中所有满足条件的数的个数
ll getans(ll l, ll r) {
    return getans(r) - getans(l - 1);

}

ll solve(ll a, ll b, ll k){
  ll l = a - 1;
  ll r = b + 1;
  ll lans = getans(a - 1);
  while(l + 1 < r){
    ll mid = (l + r)>>1;
    if(getans(mid) - lans >= k){
      r = mid;
    }else{
      l = mid;
    }
  }
  if(getans(r) - lans >= k){
    return r;
  }
  return -1;
}

int main()
{
  ll l, r, b, m, k;
  cin >> l >> r >> b >> m >> k;
  DpData::base = b;
  DpData::K = m;
  ll ans = solve(l, r, k);
  if(ans == -1){
    cout << "No" << endl;
  }else{
    cout << ans << endl;
  }
    return 0;
}
相关推荐
AshinGau3 小时前
Softmax 与 交叉熵损失
神经网络·算法
似水এ᭄往昔3 小时前
【C++】--AVL树的认识和实现
开发语言·数据结构·c++·算法·stl
程序员zgh4 小时前
常用通信协议介绍(CAN、RS232、RS485、IIC、SPI、TCP/IP)
c语言·网络·c++
栀秋6664 小时前
“无重复字符的最长子串”:从O(n²)哈希优化到滑动窗口封神,再到DP降维打击!
前端·javascript·算法
xhxxx4 小时前
不用 Set,只用两个布尔值:如何用标志位将矩阵置零的空间复杂度压到 O(1)
javascript·算法·面试
有意义4 小时前
斐波那契数列:从递归到优化的完整指南
javascript·算法·面试
暗然而日章4 小时前
C++基础:Stanford CS106L学习笔记 8 继承
c++·笔记·学习
有点。4 小时前
C++ ⼀级 2023 年06 ⽉
开发语言·c++
charlie1145141914 小时前
编写INI Parser 测试完整指南 - 从零开始
开发语言·c++·笔记·学习·算法·单元测试·测试