数位DP学习笔记

数位DP学习笔记

  • 什么是数位DP?

  位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数

  我们称之为朴素算法:

for(int i=l_i;i<=r_i;i++)  
        if(check(i)) ans++;  
return ans;

 

所有的算法都是为了减少运算步骤这一个基本原理来优化的,我们考虑这样暴力的优化,显然数的位数上面满足X性质,有些时候X性质并不是单单对于一个数的个体进行限制的

而是在某个限定区域里面的所有数字有一个X的限制,这意味着我们只要选取一定的方法,将这一部分数字除掉,其他位数的数字按照一定方法(规律填下去)就能够避免冗余运算

而且题目让我们求这样的数字有多少个而不是求出所有这样的数字,这样对于在数位上的Dp有一定的余地可以使用。

所以为了解决数位上面一些显然的规律递推,数位DP就应运而生了。

还有奶一口李建说数位DP NOIP2018会考!!!(不然我学什么学)

 

 

  • 怎么做数位DP的题目?(模板)

记忆化搜索大法好!

首先有这样的前缀和思想如果我们知道了[1,Ti]中满足X性质的数的个数

现在要求[Li,Ri]之间满足X性质的数的个数那么答案就是[1,Ri]中满足X性质的数个数减去[1,Li-1]满足X性质的数个数

对于一般的数位Dp的题目当前位置的取值能取和不可取取决于前面的某个数或者某个性质X',我们不妨在dfs搜索的时候

把这个性质X‘记录下来然后确定出当前位置的取值范围,然后再进行搜索

当然在这之中可能会存在冗余搜索,我们只要记忆化就行了,这样子,最后的复杂度就比暴力枚举要可观的多了!!!

# include <bits/stdc++.h>
using namespace std;
int dfs(int pos/*位置*/,/*状态*/,bool lead/*有无前导零*/,bool limt/*数位到上界标识*/)
{
    if(pos==-1) return 1;
    if (!limt&&!lead&&dp[pos][state]!=-1) return dp[pos][state];
    int up=limt?a[pos]:9;
    int ans=0;
    for (int i=0;i<=up;i++) {
        if () ...
        else if () ...
        ans+=dfs(pos-1,/*状态转移*/,lead&&i==0,limt&&i==a[pos]); 
    } 
    if (!limt&&!lead)  dp[pos][state]=ans;
    return ans;
} 
void solve(int x)
{
    int pos=0;
    while (x) a[pos++]=x%10,x/=10;
    return dfs(pos-1,/*初始状态*/,1,1);
 } 
int main()
{
    int l,r;
    while (~scanf("%d%d",&l,&r)) {
        printf("%d\n",solve(r)-solve(l-1));
    }
    return 0;
}

解释一下吧,显然数位DP也是一个DP也是存在DP的一系列限制,如无后效性等特性。

我们现在通过数位DP这个框架枚举状态而不是枚举数字了,也就是说你能够通过你记录的所有状态推出现在所有的可行解的数目

所以你设置状态的时候要包含所有可能性,不重不漏,满足所有他的子状态加起来得到的和就是母状态的值

解释一下程序吧,后面两个就不看了直接看dfs的程序:

int dfs(int pos/*位置*/,/*状态*/,bool lead/*有无前导零*/,bool limt/*数位到上界标识*/)
{
    if(pos==-1) return 1;
    if (!limt&&!lead&&dp[pos][state]!=-1) return dp[pos][state];
    int up=limt?a[pos]:9;
    int ans=0;
    for (int i=0;i<=up;i++) {
        if () ...
        else if () ...
        ans+=dfs(pos-1,/*状态转移*/,lead&&i==0,limt&&i==a[pos]); 
    } 
    if (!limt&&!lead)  dp[pos][state]=ans;
    return ans;
} 

pos的话就是当前枚举那个位子每次-1,假设数位从0开始标号那么pos=-1的时候就结束了返回1,之后是记录一些状态,满足这些状态下满足条件的值(比pos位还要低的位数这样后面推更高位的时候可以重复利用)

然后lead表示这个状态是不是包含前导零(对于一些题目有无前导零不影响计数为方便起见就不设状态,然后是limt是数位上界标识就是前面若干位已经是最大了,不用更大的范围,当前枚举最大值只需要恰好0到a[pos]就够用了)

然后后面一句话就是记忆化搜索(在相同的位置有相同的状态就有相同的答案),然后是求出这位的取值范围,然后根据题目条件判断答案可行,把可行的子状态加入到母状态的解中,最后满足没有限制时候记忆化搜索的结果。

解释一下lead和limt标识:

 

    • lead指代的是:此时的数含不含前导0(显然一开始是在前面是0的情况下做的,含前导0)
    • lim指代的是:此时这位的上面几位(到头)是不是都是最大值了这样此时当前位置最大值不能为9否则就超了!

 

  • 几个好的博客

 

  • 实战演练

 

sol:

# include <bits/stdc++.h>
# define int long long
using namespace std;
int a[20];
int dp[20][2];
int dfs(int pos,int pre,bool state,bool lim) 
// 到pos位,前一个数为pre,当前有没有限制,是否是lim
{
    if (pos==-1) return 1;
    if (!lim&&dp[pos][state]!=-1) return dp[pos][state];
    int up=lim?a[pos]:9;
    int ans=0;
    for (int i=0;i<=up;i++) {
        if (i==4) continue;
        if (pre==6&&i==2) continue;
        ans+=dfs(pos-1,i,i==6,lim&&i==a[pos]);
    } 
    if (!lim) dp[pos][state]=ans;
    return ans;
} 
int solve(int x)
{
    int o=0;
    while (x) {
        a[o++]=x%10;
        x/=10;
    }
    return dfs(o-1,0,0,1);
}
signed main()
{
    memset(dp,-1,sizeof(dp));
    while (1) {
        int l,r;
        scanf("%lld%lld",&l,&r);
        if (l==0&&r==0) break;
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}

洛谷P2657 SCOI2009 Windy数

sol:这道题和不要62这道题不同的是要处理前导是0的情况这里我们采取特判的形式,如果此时的p是在前导有0的情况下那么就把pre设置为负无穷

这样不会对后面有影响。注意这里再次强调一下lead和lim,

lead指代的是:此时的数含不含前导0(显然一开始是在前面是0的情况下做的,含前导0)

lim指代的是:此时这位的上面几位(到头)是不是都是最大值了这样此时当前位置最大值不能为9否则就超了!

# include <bits/stdc++.h>
# define int long long
using namespace std;
int dp[20][10],a[20];
int dfs(int pos,int pre,bool lead,bool lim)
{
    if (pos==-1) return 1;
    if (!lim&&!lead&&dp[pos][pre]!=-1) return dp[pos][pre];
    int up=lim?a[pos]:9;
    int ans=0;
    for (int i=0;i<=up;i++) {
        if (abs(i-pre)>=2) {
            int p=lead&&i==0?-0x7f7f7f7f:i;
            ans+=dfs(pos-1,p,lead && i==0,lim && i==a[pos]);
        }
    }
    if (!lim&&!lead) dp[pos][pre]=ans;
    return ans;
}
int solve(int x)
{
    int pos=0;
    while (x) {
        a[pos++]=x%10;
        x/=10;
    } 
    return dfs(pos-1,-0x7f7f7f7f,1,1);
}
signed main()
{
    memset(dp,-1,sizeof(dp));
    int l,r;
    scanf("%lld%lld",&l,&r);
    printf("%lld\n",solve(r)-solve(l-1));
    return 0;
} 
  •  数位DP进阶题目选讲
   P2235 [HNOI2002]Kathy函数
   P2518 [HAOI2010]计数
   P3281 [SCOI2013]数数
   P3303 [SDOI2013]淘金
 P3413  SAC#1 - 萌数
 P4317  花神的数论题

 

转载于:https://www.cnblogs.com/ljc20020730/p/9897187.html

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值