今天给大家带来的是关于数位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,注意题意,不要硬套模版
今天的分享就到这里,希望大家多多关注。