动态规划-数位DP

今天给大家带来的是关于数位dp的相关应用题目,大体的思路很相同,关键是:

1.如何理解记忆化搜素这个过程,如何去实现这个过程。

2.以及对于dfs转移的理解。

3.对于题目条件的理解重而选择正确合适的dp数组

问题描述

2023年举办了第 14 届蓝桥杯,小蓝在这一年拿到了国一,因此他将这一年定为自己的幸运年。现在小蓝有一个问题,如果一个正整数包含232023 或者包含 14,这个数就是幸运的。

现在他想知道在区间 [l,r]之间有多少个幸运数字,你可以帮他求出来吗?

输入格式

输入二个正整数 l,r代表区间范围。

输出格式

输出一个正整数 x,代表幸运数字的个数。

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int a[20];ll dp[20][2][10][10][10][10][2];
ll dfs(int pos,bool limit,bool snt,int p1,int p2,int p3,int p4,int cnt)
{
    // 修改检测条件为2023和14
    if ((p1==3 && p2==2 && p3==0 && p4==2) || (p1==4 && p2==1)) cnt=1;
    if(pos<0)return cnt;
    if(!limit&&dp[pos][snt][p1][p2][p3][p4][cnt]!=-1)return dp[pos][snt][p1][p2][p3][p4][cnt];
    ll res=0;
    int up=limit?a[pos]:9;
    for(int i=0;i<=up;i++)
    {
        res+=dfs(pos-1,limit&&(i==up),snt||(i>0),i,p1,p2,p3,cnt);
    }
    if(!limit)dp[pos][snt][p1][p2][p3][p4][cnt]=res;
    return res;
}
ll f(ll x)
{
    int pos=0;
    while(x){a[pos++]=x%10;x/=10;}
    return dfs(pos-1,true,false,0,0,0,0,0);
}
int main()
{
    memset(dp,-1,sizeof dp);
    ll a,b;
    cin>>a>>b;
    cout<<f(b)-f(a-1)<<"\n";
    return 0;
}

这道题要注意这几个点,也是我第一次做的时候错误的地方:

1.首先是运行错误,dfs最后不要忘记返回res,还有就是dfs出口的记忆化不要放到出口的前面,会造成错误。

2.关键就是高位在数组位数大的地方,这一点要统一,所以p1代表的是低位,不要和高位相混淆。

题目描述

小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗?

输入描述

输入一行包含两个整数 N 和 K。

输出描述

输出一个整数表示答案。

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
ll dp[70][60];
int k;
ll a[70];
ll dfs(int pos,bool limit,int cnt){
  if(pos<0)return (cnt==k);
  if(!limit&&dp[pos][cnt]!=-1)return dp[pos][cnt];
  ll res=0;
  int up=limit?a[pos]:1;
  for(int i=0;i<=up;i++){
    res+=dfs(pos-1,limit&&(i==up),cnt+(i==1));
  }
  if(!limit)dp[pos][cnt]=res;
  return res;
}
ll f(ll c){
  int pos=0;
  while(c){
    a[pos++]=(c&1),c>>=1;
  }
  return dfs(pos-1,true,0);
}
int main()
{
  memset(dp,-1,sizeof(dp));
  ll n;
  cin>>n>>k;
  cout<<f(n)<<'\n';
  return 0;
}

这道题将2进制和数位dp相结合,小心2处:

1.limit初始化位true;

2.对于2进制判断是否为1,limit?a[pos]:1这里取1而不是9,注意题意,不要硬套模版

今天的分享就到这里,希望大家多多关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值