r HDU - 3709 Balanced Numbe(数位dp解析)

2023-10-27

题目链接https://vjudge.net/contest/355127#problem/C
Problem Description
A balanced number is a non-negative integer that can be balanced if a pivot is placed at some digit. More specifically, imagine each digit as a box with weight indicated by the digit. When a pivot is placed at some digit of the number, the distance from a digit to the pivot is the offset between it and the pivot. Then the torques of left part and right part can be calculated. It is balanced if they are the same. A balanced number must be balanced with the pivot at some of its digits. For example, 4139 is a balanced number with pivot fixed at 3. The torqueses are 4* 2 + 1 * 1 = 9 and 9*1 = 9, for left part and right part, respectively. It’s your job to calculate the number of balanced numbers in a given range [x, y].
Input
The input contains multiple test cases. The first line is the total number of cases T (0 < T ≤ 30). For each case, there are two integers separated by a space in a line, x and y. (0 ≤ x ≤ y ≤ 1018).
Output
For each case, print the number of balanced numbers in the range [x, y] in a line.

Sample Input

2
0 9
7604 24324 

Sample Output

10
897

翻译
求给定区间 [l,r] 内平衡数的个数
平衡数是指,某个数以某位作为支点,此位左边的 数字*距离 的和与右边相等
例如:4139,以3位支点,左边的距离之和: 4* 2 + 1 * 1,右边的距离之和:9 *1,两边相等,所以4139为平衡数

引入数位dp
数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数

传入的参数

  1. 前导0标记lead

例如我们要从 [0,1000] 找任意相邻两数相等的数,显然 111,222,888 等等是符合题意的数
但是我们发现右端点1000 是四位数
因此我们搜索的起点是 0000 ,这样三位数的记录都变成了如 0111,0222,0888 等等
而这种情况下如果我们直接找相邻位相等,则 0000 符合题意,但是 0111,0222,0888 都变成不符合题意了
所以我们要引入前导0标记

如果当前位 lead=1 而且当前位也是0,那么当前位也归为前导0, pos+1 继续搜;
如果当前位 lead=1 但当前位不是0,则本位作为当前数的最高位, pos+1 继续搜;

LL dfs(int pos,int pre,int st,....,int lead,int limt)
{
    if(pos>len)
        return st;/*剪枝*/
    if(dp[pos][pre][st]..[...]!=-1&&!limt&&!lead)
        return dp[pos][pre][st]..[...];
    LL res=0;/*暂时记录当前的方案数*/
    int up=limt?a[len-pos+1]:9;
    for(int i=0; i<=up; i++)
    {
        if(!i&&lead)
            res+=dfs(...,...,i==up&&limt);/*有前导0,并且当前位也是0*/
        else if(i&&lead)
            res+=dfs(...,...,i==up&&limt);/*有前导0,但当前位不是0,当前位就是最高位*/
        else if(/*根据题意而定的判断*/)
            res+=dfs(...,.....,i==up&&limt)
        }
}
  1. 最高位标记limt

在搜索数位时,搜索范围可能发生变化
举个例子:我们在搜索 [0,555] 的数时,显然最高位搜索范围是 0 ~ 5 ,而后面的位数的取值范围会根据上一位发生变化:
最高位是 1 ~ 4 时,第二位取值为 [0,9]
最高位是 5 时,第二位取值为 [0,5] (再往上取就超出右端点范围了)

为了分清这两种情况,我们引入了 limt 标记:

若当前位 limit=1 而且已经取到了能取到的最高位时,下一位 limit=1 ;(limt=1表示有限制,limt=0表示没有限制
若当前位 limit=1 但是没有取到能取到的最高位时,下一位 limit=0 ;
若当前位 limit=0 时,下一位 limit=0 。
我们设这一位的标记为 limit ,这一位能取到的最大值为 res ,则下一位的标记就是 i==res && limit ( i 枚举这一位填的数)

  1. dp值的记录和使用

数位dp是在记忆化搜索的框架下进行的,每当找到一种情况我们就可以这种情况记录下来,等到搜到后面遇到相同的情况时直接使用当前记录的值。
dp数组的下标表示的是一种状态,只要当前的状态和之前搜过的某个状态完全一样,我们就可以直接返回原来已经记录下来的dp值。
再举个例子
假如我们找 [0,123456] 中符合某些条件的数
假如当我们搜到1000时,dfs从下返上来的数值就是当前位是第 5 位,前一位是 0 时的方案种数,搜完这位会向上反,这是我们可以记录一下:当前位第 5 位,前一位是 0 时,有的方案种数
当我们继续 搜到 1010时,我们发现当前状态又是搜到了第 5 位,并且上一位也是 0 ,这与我们之前记录的情况相同,这样我们就可以不继续向下搜,直接把上次的dp值返回就行了。

返回的dp值必须和当前处于完全一样的状态,这就是为什么dp数组下标要记录 pos,pre 等参量了。

接着上面的例子,范围 [0,123456]
如果我们搜到了 1234 ,我们能不能直接返回之前记录的:当前第 5 位,前一位是 4 时的dp值?
答案是否定的
我们发现,这个状态的dp值被记录时,当前位也就是第 5 位的取值是 [0,9] ,而这次当前位的取值是 [0,5] ,方案数一定比之前记录的dp值要小。
当前位的取值范围为什么会和原来不一样呢?
如果你联想到了之前所讲的知识,你会发现:现在的 limit=1 ,最高位有取值的限制。
因此我们可以得到一个结论:当 limit=1 时,不能记录和取用dp值
完整代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
int a[20];
LL dp[20][20][2000];
int tot;
LL dfs(int pos,int x,int sta,int limt)
{
    if(sta<0)/*支点两边的和<0,相当于剪枝*/
        return 0;
    if(pos==0)/*每一位都枚举完了,如果sta==0,则返回1,否则返回0*/
        return sta==0;
    if(!limt&&dp[pos][x][sta]!=-1)/*最高位没有限制*/
        return dp[pos][x][sta];
    int up=limt?a[pos]:9;
    LL res=0;
    for(int i=0; i<=up; i++)
    {
        int nex=sta+i*(pos-x);/*x是支点的下标,起初pos为数的总长度,pos-x>0,随着pos的减小,pos-x<0满足了支点两边一正一负,状态累加,状态sta=0是目标状态*/
        res+=dfs(pos-1,x,nex,limt&&(i==up));
    }
    if(!limt)
        dp[pos][x][sta]=res;/*位数为pos,支点为x,两边的支点和为sta的个数*/
    return res;
}
LL solve(LL n)
{
    tot=0;
    while(n)
    {
        a[++tot]=n%10;
        n/=10;
    }
    LL sum=0;
    for(int i=1; i<=tot; i++)
        sum+=dfs(tot,i,0,1);/*数的总长度是tot,把第i为当成是支点,两边的支点和为0的方案数*/
    return sum-tot+1;/*00,000,.....,*/
}
int main()
{
    int t;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&t);
    while(t--)
    {
        LL x,y;
        scanf("%lld%lld",&x,&y);
        printf("%lld\n",solve(y)-solve(x-1));/*相当于0~r区间内的数减去0~(l-1)区间内的数*/
    }
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

r HDU - 3709 Balanced Numbe(数位dp解析) 的相关文章

  • 洛谷 P1026 [NOIP2001 提高组] 统计单词个数

    题目描述 给出一个长度不超过 200 的由小写英文字母组成的字母串 该字串以每行 20 个字母的方式输入 且保证每行一定为 20 个 要求将此字母串分成 k 份 且每份中包含的单词个数加起来总数最大 每份中包含的单词可以部分重叠 当选用一个
  • 2023华为OD机试真题Java实现【士兵过河/动态规划】

    题目内容 一支N个士兵的军队正在趁夜色逃亡 途中遇到一条湍急的大河 敌军在T的时长后到达河面 没到过对岸的士兵都会被消灭 现在军队只找到了1只小船 这船最多能同时坐上2个士兵 1 当1个士兵划船过河 用时为 a i 0 lt i lt N
  • 动态规划问题——最长上升子序列(LIS)(一)

    原文转载自我的博客benym cn 推荐链接 动态规划问题 最长上升子序列 LIS 二 动态规划问题 最长上升子序列 LIS 三 如 求 2 7 1 5 6 4 3 8 9 的最长上升子序列 我们定义d i i 1 n 来表示前i个数以A
  • 入门级动态规划五步法(斐波那契数)

    1 确定dp数组 dp table 以及下标的含义 2 确定递推公式 3 dp数组如何初始化 4 确定遍历顺序 5 举例推导dp数组 class Solution def fib self n int gt int if n 0 retur
  • 国王和金矿问题

    国王和金矿问题 描述 有一个国家发现了max n座金矿 参与挖矿工人的总数是max people人 每座金矿的黄金储量不同为一维数组gold 需要参与挖掘的工人数也不同为一维数组peopleNeed 每座金矿要么全挖 要么不挖 不能派出一半
  • 【类】二维dp:动态规划背包问题

    dp n m 含义就是 当有n种物品时且背包有m容量时 这个背包能产生的最大价值 状态转换关系是 dp n m dp n 1 m dp n 1 m 新物品重量 意思就是 当面对新来的一个物品时 求这个情况下 背包能产生的最大价值 相当于求下
  • 力扣动态规划专题(一)背包理论基础 基础动规题 动规注意点 步骤及C++实现

    文章目录 动态规划 509 斐波那契数 五步骤 代码 70 爬楼梯 五步骤 代码 746 使用最小花费爬楼梯 五步骤 代码 扩展 62 不同路径 动态规划 数论 63 不同路径 II 五步骤 代码 343 整数拆分 五步骤 代码 96 不同
  • 动态规划算法解决背包问题(Java实现)

    文章收藏的好句子 你在书本上花的任何时间 都会在某一个时刻给你回报 目录 1 动态规划算法的概述 2 背包问题 3 动态规划算法解决背包问题 3 1 不可重复装入商品 3 2 思路分析 1 动态规划算法的概述 1 动态规划算法的思想是 将大
  • 滑雪(记忆化搜索)

    题目 题解 记忆化搜索模板题 记忆化搜索的核心 本质是带剪枝的深搜 当某点的dp已赋值时 返回该值 其他情况进行深度搜索 模板 dfs u点 if u点的 dp 已经有值了 return u点的 dp 值 else 说明第一次到达u 则为u
  • 0动态规划中等 NC206 跳跃游戏(二)

    NC206 跳跃游戏 二 描述 给定一个非负整数数组nums 假定最开始处于下标为0的位置 数组里面的每个元素代表下一跳能够跳跃的最大长度 如果可以跳到数组最后一个位置 请你求出跳跃路径中所能获得的最多的积分 1 如果能够跳到数组最后一个位
  • 字节跳动笔试---字母交换,最多m次

    参考 https blog csdn net cxzzxc123456 article details 79058419 编码题 字符串S由小写字母构成 长度为n 定义一种操作 每次都可以挑选字符串中任意的两个相邻字母进行交换 询问在至多交
  • 2023华为OD机试真题【星际篮球争霸赛/动态规划】

    题目描述 在星球争霸篮球赛对抗赛中 最大的宇宙战队希望每个人都能拿到MVP MVP的条件是单场最高分得分获得者 可以并列所以宇宙战队决定在比赛中尽可能让更多队员上场 并且让所有得分的选手得分都相同 然而比赛过程中的每1分钟的得分都只能由某一
  • OD机试题目【计算网络信号】

    网络信号经过传递会逐层衰减 且遇到阻隔物无法直接穿透 在此情况下需要计算某个位置的网络信号值 注意 网络信号可以绕过阻隔物 array m n 的二维数组代表网格地图 array i j 0代表i行j列是空旷位置 array i j x x
  • 华为机试题103-Redraiment的走法

    描述 Redraiment是走梅花桩的高手 Redraiment可以选择任意一个起点 从前到后 但只能从低处往高处的桩子走 他希望走的步数最多 你能替Redraiment研究他最多走的步数吗 数据范围 每组数据长度满足1 n 200 数据大
  • 动态规划之多重背包模型

    前置知识 01背包问题 动态规划之01背包模型 如何何何的博客 CSDN博客 完全背包问题 动态规划之完全背包模型 如何何何的博客 CSDN博客 多重背包问题 给定一个有一定容量的背包 和 n 个物品 每个物品有 si 件 每个物品有其对应
  • 第14届蓝桥杯C++B组省赛

    文章目录 A 日期统计 B 01 串的熵 C 冶炼金属 D 飞机降落 E 接龙数列 F 岛屿个数 G 子串简写 H 整数删除 I 景区导游 J 砍树 今年比去年难好多 Update 2023 4 10 反转了 炼金二分没写错 可以AC了 U
  • 最大k乘积问题--动态规划

    问题 问题描述 设x是一个n位十进制整数 如果将x划分为k段 则可得到k个整数 这k个整数的乘积称为x的一个k乘积 试设计一个算法 对于给定的x和k 求出x的最大k乘积 编程任务 对于给定的x和k 编程计算x的最大k 乘积 示例 Sampl
  • 算法设计与实现--动态规划篇

    什么是动态规划算法 动态规划算法是一种求解复杂问题的方法 通过将原问题分解为相对简单的子问题来求解 其基本思想是将待求解的问题分解为若干个子问题 阶段 按顺序求解子阶段 前一子问题的解 为后一子问题的求解提供了有用的信息 在求解任一子问题时
  • C/C++---------------LeetCode第509. 斐波那契数

    斐波那契数列 题目及要求 暴力递归 备忘录的递归 动态规划 题目及要求 斐波那契数 通常用 F n 表示 形成的序列称为 斐波那契数列 该数列由 0 和 1 开始 后面的每一项数字都是前面两项数字的和 也就是 F 0 0 F 1 1 F n
  • 【算法刷题】每日打卡——动态规划(1)

    背包问题 例题一 有 N件物品和一个容量是 V 的背包 每件物品只能使用一次 第 i件物品的体积是 vi 价值是 wi 求解将哪些物品装入背包 可使这些物品的总体积不超过背包容量 且总价值最大 输出最大价值 输入格式 第一行两个整数 N V

随机推荐