算法竞赛备赛进阶之数位DP训练

news2024/12/31 6:06:53

数位DP的思想就是对每一位进行DP,计算时记忆化每一位可以有的状态,其作用是减少运算时间,避免重复计算。

数位DP是一种计数用的DP,一般就是要统计一个区间[A,B]内满足一些条件数的个数。

以1e9甚至1e18、1e100的问题为例,因为在统计情况下有很多重复的计算,数位DP实现了相同状态只计算一次,从而大幅减少运算时间。

数位DP:

技巧1:[X, Y] => f(Y) - f(X-1)

技巧2:用树进行排列

1.度的数量

1081

求给定区间[X, Y]中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和。

例如。设X=15,Y=20,K=2,B=2,择优且仅有下列三个数满足题意:

17 = 24 + 20

18 = 24 + 21

20 = 24 + 22

输入格式

第一行包含两个整数X和Y,接下来两行包含整数K和B

#include<iostream>
#include<algorithm>
#include<vector>
​
using namespace std;
​
const int N = 35;
​
int K, B;
int f[N][N];
​
void init()
{
    for(int i = 0;i < N; i++)
        for(int j = 0;j <= N; j++)
            if(!j) f[i][j] = 1;
            else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
}
​
int dp(int n)
{
    if(!n) return 0;
    
    vector<int> nums;
    while(n)
    {
        nums.push_back(n % B);
        n /= B;
    }
    
    int res = 0;
    int last = 0;
    
    for(int i = nums.size() - 1;i >= 0; i--)
    {
        int x = nums[i];
        if(x)//求左边的分支
        {
            res += f[i][K - last];
            if(x > 1)
            {
                if(K - last - 1 >= 0) res += f[i][K - last - 1];
                break;
            }
            else 
            {
                last++;
                if(last > K) break;
            }
        }
        if(!i && last == K) res++; // 最右侧分支上的方案
    }
    
    return res;
}
​
int main()
{
    init();
    
    int l, r;
    cin >> l >> r >> K >> B;
    
    cout << dp(r) - dp(r - 1) << endl;
    return 0;
}

2.数字游戏

科协里最近很流行数字游戏。

某人命名了一种不降数,这种数字必须满足从左到右各位数字呈现非下降关系,如123,446

现在大家决定玩一个游戏,指定一个整数闭区间[a, b],问这个区间内有多少个不降数。

动态规划

  1. 状态表示

    1. 集合:所有最高位是j,且一共有i位的不降数的集合

    2. 属性:数量

  2. 状态计算

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
​
using namespace std;
​
const int N = 40;
​
int f[N][N]; // f[i][j]表示一共有i位,且最高位填j的数的个数
​
void init()
{
    for(int i = 0;i <= 9; i++) f[1][i] = 1;
    
    for(int i = 2;i < N; i++)
        for(int j = 0;j <= 9; j++)
            for(int k = j;k <= 9; k++)
                f[i][j] += f[i - 1][k];
}
​
int dp(int n)
{
    if(!n) return 1;
    
    vector<int> nums;
    while(n)
    {
        nums.push_back(n % 10);
        n /= 10;
    }
    
    int res = 0;
    int last = 0;
    for(int i = nums.size() - 1;i >= 0; i--)
    {
        int x = nums[i];
        for(int j = last;j < x; j++)
            res += f[i + 1][j];
        
        if(last > x)
            break;
        last = x;
        
        if(!i) res++;
    }
    
    return res;
}
​
int main()
{
    void init();
    
    int l, r;
    while(scanf("%d%d", &l, &r))
    {
        cout << dp[r] - dp[l - 1] << endl;
    }
    
    return 0;
}

3.Windy数

1083.Windy定义了一种Windy数:不包含前导零且相邻两个数字之差至少为2的正整数被称为Windy数。

Windy想知道,在A和B之间,包括A和B,总共有多少个Windy数?

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
​
using namespace std;
​
const int N = 11;
​
int f[N][10];
​
void init()
{
    for(int i = 0;i <= 9; i++) f[1][i] = 1;
    
    for(int i = 2;i < N; i++)
        for(int j = 0;j <= 9; j++)
            for(int k = 0;k <= 9; k++)
                if(abs(j - k) >= 2)
                    f[i][j] += f[i - 1][k];
}
​
int dp(int n)
{
    if(!n) return 0;
    
    vector<int> nums;
    while(n) nums.push_back(n % 10), n /= 10;
    
    int res = 0;
    int last = -2;
    for(int i = nums.size() - 1;i >= 0; i--)
    {
        int x = nums[i];
        for(int j = 0;j < x; j++)
            if(abs(j - last) >= 2)
                res += f[i + 1][j];
        
        if(abs(x - last) >= 2) last = x;
        else break;
        
        if(!i) res++;
    }
    
    // 特殊枚举有前导零
    for(int i = 1;i < nums.size(); i++)
        for(int j = 1;j <= 9;+)
            res += f[i][j];
    
    return res;
}
​
int main()
{
    init();
    
    int l, r;
    cin >> l >> r;
    
    cout << dp(r) - dp(l - 1) << endl;
    
    return 0;
}

4.数字游戏II

1084.由于科协里最近真的很流行数字游戏。

某人又命名了一种取模数,这种数字必须满足各位数字之和mod N 为0

现在大家又要玩游戏了,指定一个整数闭区间[a, b],问这个区间内有多少个取模数。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
​
using namespace std;
​
const int N = 11, M = 110;
​
int P;
int f[N][10][M];
​
int mod(int x, int y)
{
    return (x % y + y) % y;
}
​
void init()
{
    memset(f, 0, sizeof(f));
    
    for(int i = 0;i <= 9; i++) f[1][i][i % P]++;
    
    for(int i = 2;i < N; i++)
        for(int j = 0;j <= 9; j++)
            for(int k = 0;k < N; k++)
                for(int x = 0;x <= 9; x++)
                    f[i][j][k] += f[i - 1][x][mod(k - j, P)];
}
​
int dp(int n)
{
    if(!n) return 1;
    
    vector<int> nums;
    while(n) nums.push_back(n % 10), n /= 10;
    
    int res = 0;
    int last = 0;
    for(int i = nums.size() - 1;i >= 0; i--)
    {
        int x = nums[i];
        for(int j = 0;j < x; j++)
            res += f[i + 1][j][mod(-last, P)];
        
        last += x;
        
        if(!i && last % P == 0) res++;
    }
    
    return res;
}
​
int main()
{
    int l, r;
    while(cin >> l >> r >> P)
    {
        init();
        
        cout << dp[r] - dp[l - 1] << endl;
    }
    
    return 0;
}

5.不要62

1085.杭州人称那些傻乎乎黏兮兮的人为62。

杭州交通管理局经常会扩充一些的士车牌照。附近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士机和乘客的心理障碍,更安全地服务大众。

不吉利的数字为所有含有4或62的号码。如:62315,73418,88914都属于不吉利号码。但是,61152虽然含有6和2,但不是逐号,所以不属于不吉利数字之列。

你的任务是,对于每次给出的一个牌照号区号[n, m],推断出交管局今后又要实际上给多少辆的士车上牌照了。

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
​
using namespace std;
​
const int N = 9;
​
int f[N][10];
​
void init()
{
    for(int i = 0;i <= 9; i++)
        if(i != 4)
            f[1][i] = 1;
    
    for(int i = 2;i < N; i++)
        for(int j = 0;j <= 9; j++)
        {
            if(j == 4) continue;
            for(int k = 0;k <= 9; k++)
            {
                if(k == 4 || j == 6 && k == 2)
                    continue;
                f[i][j] += f[i - 1][k];
            }
        }
}
​
int dp(int n)
{
    if(!n) return 1;
    
    vector<int> nums;
    while(n) nums.push_back(n % 10), n /= 10;
    
    int res = 0;
    int last = 0;
    for(int i = nums.size() - 1;i >= 0; i--)
    {
        int x = nums[i];
        for(int j = 0;j < x; j++)
        {
            if(j == 4 || last == 6 && j == 2) continue;
            res += f[i + 1][j];
        }
        
        if(x == 4 || last == 6 && x == 2) break;
        last = x;
        
        if(!i) res++;
    }
    
    return res;
}
​
int main()
{
    init();
    
    int l, r;
    while(cin >> l >> r, l || r)
    {
        cout << dp[r] - dp[l - 1] << endl;
    }
    
    return 0;
}

6.恨7不成妻

1086.DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌!

吉哥观察了214和77这两个数,发现:   2+1+4=7   7+7=72   77=711 最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!

什么样的数和7有关呢?

如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关—— 1、整数中某一位是7; 2、整数的每一位加起来的和是7的整数倍; 3、这个整数是7的整数倍;

现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
​
using namespace std;
​
typedef long long LL;
​
const int N = 20, P = 1e9 + 7;
​
struct F
{
    int s0, s1, s2;
}f[N][10][7][7];
​
int power7[N], power9[N];
​
int mod(LL x, int y)
{
    return (x % y + y) % y;
}
​
void init()
{
    for(int i = 0;i <= 9; i++)
    {
        if(i == 7) continue;
        auto &v = f[1][i][i % 7][i % 7];
        v.s0++;
        v.s1 += i;
        v.s2 += i * i;
    }
    
    LL power = 10;
    for(int i = 2;i < N; i++, power *= 10)
        for(int j = 0;j < N; j++)
        {
            if(j == 7) continue;
            for(int a = 0;a < 7; a++)
                for(int b = 0;b < 7; b++)
                    for(int k = 0;k <= 9; k++)
                    {
                        if(k == 7) continue;
                        auto &v1 = f[i][j][a][b], &v2 = f[i - 1][k][mod(a - j * (power % 7), 7)][mod(b - j, 7)];
                        v1.s0 = (v1.s0 + v2.s0) % P;
                        v1.s1 = (v1.s1 + j * (power % P) * v2.s0 + v2.s1) % P; 
                        v1.s2 = (v1.s2 + j * j * (power % P) % P * (power % P) % P * v2.s0) % P + 2 * j * (power % P) % P * v2.s1 % P + v2.s2) % P;
                    }
        }
    
    power7[0] = power9[0] = 1;
    for(int i = 1;i < N; i++)
    {
        power7[i] = power7[i - 1] * 10 % 7;
        power9[i] = power9[i - 1] * 10 % P;
    }
}
​
F get(int i, int j, int a, int b)
{
    int s0 = 0, s1 = 0, s2 = 0;
    for(int x = 0;x < 7; x++)
        for(int y = 0;y < 7; y++)
        {
            if(x == a || y == b) continue;
            auto v = f[i][j][x][y];
            s0 = (s0 + v.s0) % P;
            s1 = (s1 + v.s1) % P;
            s2 = (s2 + v.s2) % P;
        }
    return {s0, s1, s2};
}
​
int dp(LL n)
{
    if(!n) return 0;
    
    LL backup_n = n % P;
    vector<int> nums;
    while(n) nums.push_back(n % 10), n /= 10;
    
    int res = 0;
    LL last_a = 0, last_b = 0;
    for(int i = nums.size() - 1;i >= 0; i--)
    {
        int x = nums[i];
        for(int j = 0;j < x; j++)
        {
            if(j == 7) continue;
            int a = mod(-last_a % 7 * power7[i + 1], 7);
            int b = mod(-last_b, 7);
            
            auto v = get(i + 1, j, a, b);
            res = (res + (last_a % P) * (last_a * P) % P * power9[i + 1] % P * power9[i + 1] % P * v.s0 % P + 2 (last_a % P) % P * power9[i + 1] % P * v.s1 % P + v.s2) % P;
        }
        
        if( x == 7) break;
        last_a = last_a * 10 + x;
        last_b += x;
        
        if(!i && last_a % 7 && last_b % 7) res += (res + backup_n * backup_n) % P;
    }
    
    return res;
}
​
int main()
{
    init();
    
    int T;
    cin >> T;
    while(T--)
    {
        int l, r;
        cin >> l >> r;
        cout << mod(dp(r) - dp(l - 1), P) << endl;
    }
    
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1388771.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker 容器之间的互相通信

Docker容器之间的互相通信 步骤一&#xff1a;创建自定义网络 首先&#xff0c;我们需要创建一个自定义网络&#xff0c;以便容器可以连接到这个网络上&#xff0c;从而实现互相通信。在命令行中执行以下命令&#xff1a; # 创建 docker network create ddz # 查看 docker n…

洛谷 P1523 旅行商简化版【线性dp+npc问题简化版】

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1523 题目背景 欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解&#xff0c;但是有…

2024 年企业要增强反脆弱性,IT 能够做什么?

新冠疫情被称为黑天鹅事件&#xff0c;而“黑天鹅”这个词的创造者纳西姆尼古拉斯塔勒布在另一本书《反脆弱&#xff1a;从不确定性中获益》&#xff08; CSDN博主读书笔记《反脆弱&#xff1a;从不确定性中获益》 &#xff09;中&#xff0c;则给出了面对随时可能出现的黑天鹅…

玖章算术NineData通过阿里云PolarDB产品生态集成认证

近日&#xff0c;玖章算术旗下NineData 云原生智能数据管理平台 (V1.0&#xff09;正式通过了阿里云PolarDB PostgreSQL版 (V11)产品集成认证测试&#xff0c;并获得阿里云颁发的产品生态集成认证。 测试结果表明&#xff0c;玖章算术旗下NineData数据管理平台 (V1.0&#xff…

Python源码23:海龟画图turtle画小狗狗

---------------turtle源码集合--------------- Python教程43&#xff1a;海龟画图turtle画小樱魔法阵 Python教程42&#xff1a;海龟画图turtle画海绵宝宝 Python教程41&#xff1a;海龟画图turtle画蜡笔小新 Python教程40&#xff1a;使用turtle画一只杰瑞 Python教程39…

❤ Uniapp使用一(文档和 API 篇)

❤ Uniapp使用一&#xff08;文档和 API 篇&#xff09; 一、介绍 uni-app官网&#xff1a;https://uniapp.dcloud.io/api/media/image?idpreviewimage 微信小程序官网&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.previewImage.html …

使用WAF防御网络上的隐蔽威胁之SQL注入攻击

SQL注入攻击是一种普遍存在且危害巨大的网络安全威胁&#xff0c;它允许攻击者通过执行恶意的SQL语句来操纵或破坏数据库。 这种攻击不仅能够读取敏感数据&#xff0c;还可能用于添加、修改或删除数据库中的记录。因此&#xff0c;了解SQL注入攻击的机制及其防御策略对于保护网…

Spring基于AOP(面向切面编程)开发

概述 AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续&#xff0c;是软件开发中的一个热点&#xff0c;也是Spring框架中的一个重要内容&…

使用WAF防御之网络上的隐蔽威胁(XSS攻击)

跨站脚本攻击&#xff08;XSS&#xff09;是一种常见且危险的威胁。它允许攻击者在用户浏览器上执行恶意脚本&#xff0c;窃取信息、篡改网页内容&#xff0c;甚至劫持用户会话。 什么是XSS攻击 定义&#xff1a;XSS攻击是一种代码注入技术&#xff0c;攻击者通过在目标网站上…

练习题 删除链表的倒数第N个结点

题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[]示例 3&#…

潍坊数字孪生元宇宙赋能智能制造,助力工业制造业数字化转型

潍坊工业元宇宙数字孪生赋能智能制造&#xff0c;助力工业制造业数字化转型。在当今数字化时代&#xff0c;工业智能制造已成为制造业发展的必然趋势。潍坊市作为山东省的重要工业基地&#xff0c;积极探索数字孪生技术在工业智能制造领域的应用&#xff0c;为制造业企业数字化…

ant-design-vue Notification 通知提醒框 内容换行

直接上代码 const msg errArr.map((message, index) > ${index 1}. ${message};) notification.open({message: ${statu.moduleName} 告警信息,description: () > {// 将msg所有;替换为\n换行符const res msg.replaceAll(;, \n)return h(pre,{style: {overflow: scro…

Transformer简单理解

目录 一、CNN存在的问题&#xff1a;二.Transformer整理架构分析&#xff1a;1.Linear Projection of Flattened Patches层形成Patch&#xff1a;2.对每个Patch进行位置编码Position Embedding&#xff1a;3.Transformer Encoder: 三.公式解读&#xff1a; 一、CNN存在的问题&a…

为什么使用 atan2(sin(z), cos(z)) 进行角度归一化?

文章目录 为什么使用 atan2(sin(z), cos(z)) 进行归一化&#xff1f;为什么归一化后的角度等于原始角度&#xff1f; atan2 方法返回 -π 到 π 之间的值&#xff0c;代表点 (x, y) 相对于正X轴的偏移角度。这个角度是逆时针测量的&#xff0c;以弧度为单位。关于 atan2 函数为…

【JAVA WEB】 Filter过滤器详解

目录 1&#xff0c;Filter 1.1 Filter概述 1.2 Filter快速入门 1.2.1 开发步骤 1.2.2 代码演示 1.3 Filter执行流程 1.4 Filter拦截路径配置 1.5 过滤器链 1.5.1 概述 1.5.2 代码演示 1.5.3 问题 1.6 案例 1.6.1 需求 1.6.2 分析 1.6.3 代码实现 1.6.3.1 创建Fi…

ERP系统怎么选 企业ERP管理系统选型建议

市面上有众多的ERP系统&#xff0c;而由于不同软件供应商的发展策略不同&#xff0c;导致不同ERP系统的侧重点也不同。例如有针对企业某一类管理需求的ERP系统&#xff0c;例如财务管理软件&#xff0c;进销存管理软件&#xff0c;仓库管理软件等。还有针对企业资源整合&#x…

element-ui表单验证同时用change与blur一起验证

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 当审批时不通过审批意见要必须输入&#xff0c; 1&#xff1a;如果用change验证的话删除所有内容时报错是massage的提示&#xff0c;但是在失去焦点的时候报错就成了英文&#xff0c;如下图&#xf…

【iOS】数据存储方式总结(持久化)沙盒结构

在iOS开发中&#xff0c;我们经常性地需要存储一些状态和数据&#xff0c;比如用户对于App的相关设置、需要在本地缓存的数据等等&#xff0c;本篇文章将介绍六个主要的数据存储方式 iOS中数据存储方式&#xff08;数据持久化&#xff09; 根据要存储的数据大小、存储数据以及…

计算机毕业设计 | SpringBoot宠物店管理系统(附源码)

1&#xff0c;绪论 项目背景 我国已经成为世界第二大经济体&#xff0c;经济实力高速发展以及百姓生活水平的普遍提高&#xff0c;不断地要求企业提供更加多元化的娱乐方式&#xff0c;更加快速和方便的服务&#xff0c;因此对宠物行业也提出了更加严格的要求&#xff0c;如管…

【NI国产替代】NI‑9232,3通道,102.4 kS/s/ch,±30 V,C系列声音与振动输入模块

3通道&#xff0c;102.4 kS/s/ch&#xff0c;30 V&#xff0c;C系列声音与振动输入模块 NI‑9232可以测量来自集成电子压电(IEPE)和非IEPE传感器的信号&#xff0c;例如加速度计、转速计和接近式探针。 NI‑9232还可兼容智能TEDS传感器。\n\nNI‑9232集成了软件可选的AC/DC耦合…