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;
}
相关推荐
专注VB编程开发20年3 分钟前
压栈顺序是反向(从右往左)的,但正因为是反向压栈,所以第一个参数反而离栈顶(ESP)最近。
java·开发语言·算法
Xの哲學3 分钟前
Linux Select 工作原理深度剖析: 从设计思想到实现细节
linux·服务器·网络·算法·边缘计算
say_fall8 分钟前
C++ 类与对象易错点:初始化列表顺序 / 静态成员访问 / 隐式类型转换
android·java·开发语言·c++
Paul_092022 分钟前
golang编程题
开发语言·算法·golang
颜酱27 分钟前
用填充表格法-继续吃透完全背包及其变形
前端·后端·算法
夏秃然30 分钟前
打破预测与决策的孤岛:如何构建“能源垂类大模型”?
算法·ai·大模型
ChoSeitaku34 分钟前
16.C++入门:list|手撕list|反向迭代器|与vector对比
c++·windows·list
氷泠34 分钟前
课程表系列(LeetCode 207 & 210 & 630 & 1462)
算法·leetcode·拓扑排序·反悔贪心·三色标记法
代码or搬砖37 分钟前
JVM垃圾回收器
java·jvm·算法
老鼠只爱大米39 分钟前
LeetCode算法题详解 15:三数之和
算法·leetcode·双指针·三数之和·分治法·three sum