数位DP应用:round numbers

传送门luoguP6218
题目要求找出二进制下0的个数至少大于等于1的个数的数。
容易发现这是一道数位dp的题目,1~r的个数减去1~l-1的个数等于l~r区间的个数。我们还要关注前导零,因为前导零是不能统计到0的个数中的,当然还有最高位限制,然后记忆化搜索。
基本的都已经分析过,主要是我们该使用哪些状态来描述呢?
肯定要描述第t位的状态,既然题目要求0和1的个数,我们不妨就令另外状态为num0表示当前0的个数,num1表示当前1的个数。即dp[t][num0][num1]。
当进入dfs初始化时,前导0为true,最高位限制为true,num0,num1为0。
当搜完后,判断num0是否大于等于num1,是则+1,否则不加。
当前面是0,且当前还是0时,num0,num1都是0。
当不是时,就判断当前是1还是0,并对应的+1。
最后如果非特殊情况,dp[t][num0][num1]=ans,下次搜到时直接return。
代码实现:

#include<bits/stdc++.h>
using namespace std;

const int N=32;//2^32即大于了2e9
int dp[N][N][N],a[N],len;

int dfs(int t,int num0,int num1,int st,int limit)
{
	if(t==0) return num0>=num1;
	if(limit==0 && st==0 && dp[t][num0][num1]!=0) return dp[t][num0][num1];
	int res=limit?a[t]:1,ans=0;
	for(int i=0;i<=res;i++)
	{
		if(st && i==0) ans+=dfs(t-1,0,0,1,limit&&i==res);
		else ans+=dfs(t-1,num0+(i==0),num1+(i==1),0,limit&&i==res);
	}
	if(limit==0 && st==0) dp[t][num0][num1]=ans;
	return ans;
}

int solve(int x)
{
	len=0;
	memset(dp,0,sizeof(0));
	while(x) a[++len]=x&1,x>>=1;//取出每一二进制位
	return dfs(len,0,0,1,1);
}

int main()
{
	int l,r;
	scanf("%d%d",&l,&r);
	cout<<solve(r)-solve(l-1);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值