不止代码:区间圆数(ybtoj-数位DP)

本文探讨了一道编程题目,该题目要求统计特定范围内二进制数中1的个数。作者通过动态规划的方法,用dp[pos][s0]表示二进制数pos位有s0个0的数的个数,并给出详细转移方程。代码实现中,首先初始化dp数组,然后计算不同位置和0的个数对应的二进制数个数,最后解决边界条件并返回结果。文章适合对动态规划和二进制计数感兴趣的读者。
摘要由CSDN通过智能技术生成

题目描述

在这里插入图片描述

解析

一寸山河一寸血

理解万岁!

首先,这题统计[l,r]的个数,可以用[1,r]-[1,l-1]来实现

接下来就是如何统计出[1,n]的个数了
首先,用dp[pos][s0]来表示一个二进制pos位有s0个0的数的个数
我们允许前导零,那么转移就非常简单:

dp[0][0]=1;
dp[pos][num]=dp[pos-1][num]+dp[pos-1][num-1];

现在就要考虑如何求了
因为有前导零,所以要分为两部分来求
具体实现看代码吧

代码

#include<bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=600;
const int M=2e6+100;
int m,n;
ll dp[50][50];
void Dp(){
	dp[0][0]=1;
	for(int pos=1;pos<=31;pos++){
		dp[pos][0]=1;
		for(int num=1;num<=pos;num++){
			dp[pos][num]=dp[pos-1][num]+dp[pos-1][num-1];
		}
	}
}

int solve(int n){
	if(n==1||n==0) return 0;
	int ans=0,s[50],x=n,s0=0,s1=0,tot=0;
	while(x){
		s[++tot]=x&1;
		x /= 2;
	}
	for(int pos=tot;pos>=1;pos--){
		//最高位特判不要算 
		if(s[pos]&&tot!=pos){//有一的话:前面与n相同,pos填1 
			s0++;
			for(int i=pos-1;i>=0&&s0+i>=tot-s0-i;i--) ans+=dp[pos-1][i];
			s0--;
		}
		if(tot!=pos){//以pos作为最高位,填 1 
			for(int i=pos-1;i>=0&&i>=pos-i;i--) ans+=dp[pos-1][i];
		}
		s1+=s[pos];
		s0+=!s[pos];
	}
	if(s0>=s1) ans++;
	return ans;
}
int a,b;
int main(){
	Dp();
	scanf("%d%d",&a,&b);
	printf("%d\n",solve(b)-solve(a-1));
	return 0;
}
/*
13
100
200
1000
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值