首页 > 其他分享 >数位 dp

数位 dp

时间:2023-04-08 11:45:39浏览次数:43  
标签:last int res break ++ text dp 数位

数位 \(\text{dp}\)

前言

谨慎学习此算法。

算法讲解

AcWing 1081.度的数量

  1. 题意分析:你看到这道题,是不是无从下手?其实题目就是让我们求在 \(x \sim y\) 中,有多少个数分解成 \(B\) 进制后仅有 \(k\) 位为 \(1\),其余均为 \(0\);

  2. 考虑暴力:从 \(x\) 枚举到 \(y\),将 \(i(x \le i \le y)\) 分解为 \(B\) 进制,看是否满足条件,统计数量。因为 \(x,y \le 2^{31} - 1\),所以很显然会 \(\text{TLE}\);

  3. 分析:首先我们可以求 \(1 \sim y\) 和 \(1 \sim (x - 1)\) 中有多少个数满足条件,最终答案即为 \(\text{dp(y) - dp(x - 1)}\)。首先我们要解决此题需要知道数位 \(\text{dp}\) 的核心方法:从高位低位采用字典序来分类整批的统计。

那么这道题的思路就是:

  1. 将 \(x\) 每一位数提取出来(\(B\) 进制);

Code

vector<int> v;
while (x) {
    v.push_back(x % B);
    x /= B;
}
  1. 按字典序从高到低整批统计;

Code

int res = 0,last = 0;
for (int i = v.size() - 1; i >= 0; -- i ) {
    int t = v[i]; // 拿出第 i 位
    if (!t) {
        if (!i && last == k) ++ res;
        continue;
    }
	// 1.第 i 位填 0
    res += f[i][k - last]; // 右边填 k - last 个 1 满足条件的数的个数
	// 2.第 i 位填 1
    if (t == 1) {
        ++ last;
        if (last > k) break;
    } else if (t > 1) {
        ++ last;
        if (last > k) break;
        res += f[i][k - last];
        break;
    }
    if (!i && last == k) ++ res; // 如果此数本身满足条件,也要累加
}
  1. 初始化求 \(f_{i,j}\)

\(f_{i,0} = 1,f_{i,j} = f_{i - 1,j} + f_{i - 1,j - 1}(j \le i)\)

Code

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] + f[i - 1][j - 1];
}

AC Code of AcWing 1081.度的数量

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 35;

int x,y,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] + f[i - 1][j - 1];
}

int dp(int x) {
    if (!x) return 0;
    vector<int> v;
    while (x) {
        v.push_back(x % B);
        x /= B;
    }
    int res = 0,last = 0;
    for (int i = v.size() - 1; i >= 0; -- i ) {
        int t = v[i];
        if (!t) {
            if (!i && last == k) ++ res;
            continue;
        }
        res += f[i][k - last];
        if (t == 1) {
            ++ last;
            if (last > k) break;
        } else if (t > 1) {
            ++ last;
            if (last > k) break;
            res += f[i][k - last];
            break;
        }
        if (!i && last == k) ++ res;
    }
    return res;
}

signed main() {
    cin >> x >> y >> k >> B;
    init();
    cout << dp(y) - dp(x - 1) << endl;
    return 0;
}

\[\text{Thanks} \]

作者:\(\text{songszh}\)

标签:last,int,res,break,++,text,dp,数位
From: https://www.cnblogs.com/songszh/p/shu-wei-dp.html

相关文章

  • 关于 IDP 的五大认知误解
    内部开发者平台(IDP)是近年来在希望加快软件交付和改善开发者体验的企业中得到普及的一个概念。然而,大众对于什么是IDP以及它能为开发者和企业带来什么也有很多困惑和误解。在这篇文章中,我们将尝试解开一些关于平台工程以及IDP的常见误解,以及关于企业该如何避免进入这些误区给出......
  • LightOJ - 1044 Palindrome Partitioning(DP)
    题目大意:给你一个字符串,要求你对字符串进行划分,使得划分出来的子串都是回文串,且子串数量达到最小解题思路:用dp[i]表示前i个字符划分成回文串,需要划分成多少个部分接着枚举j,如果[i,j]回文,那么dp[i]=min(dp[i],dp[j-1]+1)#include<cstdio>#include<cstring>#include<al......
  • UVA - 10003 Cutting Sticks 区间DP
    题目大意:给出一根木棒长l,上面有k个点,要求从这些点切割,每次切割的代价是当前要切割的木棒的长度,求最小的代价解题思路:区间DP:区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个......
  • ZOJ - 3469 Food Delivery(区间DP)
    题目大意:有一个餐厅,在X这个位置,送餐速度为v的-1次方,有N个顾客,分别在pos位置,每个顾客都有一个displeasure值,当餐送到该顾客手上时,该顾客的displeasure总值为displeasure值*到手时间问所有顾客的最小displeasure总值和是多少解题思路:首先按位置排个序设dp[i][j][0]为[i,j]这个区......
  • POJ - 1651 Multiplication Puzzle(区间dp)
    题目大意:给你N个数,每次可以选择一个数进行剔除(第一个和最后一个不能选择),选出该数后,sum+=该数左边的数*该数*该数右边的数问最小的sum是多少解题思路:用dp[i][j]表示[i,j]区间被剔除得只剩下i,j的最小sumdp[i][j]=dp[i][k]+dp[k][j]+num[i]*num[k]*num[j]#include......
  • CodeForces - 149D Coloring Brackets(区间DP)
    题目大意:给你一个符合括号规则的字符串,现在要求你将这些括号染色,染色规则如下1.一个括号要么被染成红色,要么被染成蓝色,要么不染2.相邻的括号的颜色不能相同(可以同为无色)3.成对的括号只能有一个被染色问染色的方案数解题思路:0表示不染,1表示红色,2表示蓝色那么成对的括号......
  • POJ - 2955 Brackets(区间dp)
    题目大意:给出一个括号字符串,问这个字符串中符合规则的最长子串的长度解题思路:区间dp,用dp[i][j]表示[i,j]这个区间内有符合规则的最长子串的长度如果str[i]和str[j]能构成()或者[],那么dp[i][j]=dp[i+1][j-1]+2剩下的情况就是dp[i][j]=max(dp[i][j],dp[i][k]+dp[k......
  • POJ - 3666 Making the Grade(DP)
    题目大意:给你一个数组A,要求将这个数组变成数组B,使得Sum(abs(A[i]-B[i]))达到最小,且B是单调的解题思路:因为答案要求输出单调非递增或者单调非递减的的任意一个,那就只考虑单调非递增吧,因为两个的思路是相同的如果要变化的话,且变化的值要达到最小的话,那么只能变成和前一个相同或者......
  • POJ - 3186 Treats for the Cows(DP)
    题目大意:给你一个数组,每次你可以取两个数中的一个进行操作,要么取数组的第一个,要么数组的最后一个(取完之后,该数删除)假设取出来的数组组成了A现在要求使Sum=A[1]*1+A[2]*2+A[3]*3…+A[n]*n达到最大解题思路:用dp[i][j]表示前面取了i个,后面取了j个最大值则转......
  • POJ - 1661 Help Jimmy(DP)
    题目大意:中文题解题思路:因为是垂直下降,所以就可以将问题转变成从降落的平台到地面的最小时间因为只能从平台的左边或者右边才可以离开平台,所以可以分别计算出从左边和从右边到达地面的最短时间,并记录#include<cstdio>#include<cstring>#include<algorithm>usingnamespace......