入 门 数 位 第 二 题 入门数位第二题

Ⅰ . 定 义 d p [ i ] [ j ] [ k ] 位 枚 举 到 二 进 制 第 i 位 有 j 个 0 和 k 个 1 的 合 法 情 况 ( 高 位 到 低 位 ) Ⅰ.定义dp[i][j][k]位枚举到二进制第i位有j个0和k个1的合法情况(高位到低位) .dp[i][j][k]ij0k1()

Ⅱ . 分 解 数 字 时 按 照 二 进 制 分 解 , 因 为 这 里 要 处 理 的 只 是 二 进 制 的 0 和 1 Ⅱ.分解数字时按照二进制分解,因为这里要处理的只是二进制的0和1 .,01

Ⅲ . 由 于 高 位 为 0 , 是 前 导 零 , 不 应 加 入 计 数 , 所 以 d f s 的 信 息 多 一 个 是 否 是 前 导 零 Ⅲ.由于高位为0,是前导零,不应加入计数,所以dfs的信息多一个是否是前导零 .0,,,dfs

更细节的注释都在代码里

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
int dp[35][35][35],a[35],n,m;
//从左到右分别表示:当前枚举的二进制位数(高到低),二进制0的个数,二进制1的个数,是否还在前导零,当前数字是否比n小 
int dfs(int len,int c0,int c1,int up0,int limit)
{
	if( len<1 )
	{
		if( up0||c0>=c1 )	return 1;//数字是0或者二进制0大于二进制1
		return 0; 
	}
	if( !limit&&!up0&&dp[len][c0][c1]!=-1 )	return dp[len][c0][c1];
	//直接返回以前计算出的枚举到len,c0个 
	int last=limit?a[len]:1,sumn=0;
	for(int i=0;i<=last;i++)
	{
		if( up0 )//有前导零 
		{
			if( i )	sumn+=dfs(len-1,0,1,0,limit&&(i==a[len]));
			else	sumn+=dfs(len-1,0,0,1,limit&&(i==a[len]));
		}
		else
		{
			if( i )	sumn+=dfs(len-1,c0,c1+1,0,limit&&(i==a[len]));
			else	sumn+=dfs(len-1,c0+1,c1,0,limit&&(i==a[len]));
		}
	}
	//当没有限制也没有前导零才行 
	if( !limit&&!up0 )	dp[len][c0][c1]=sumn;
	return sumn;
}
/*
关于limit的说明
由于dfs是从高位二进制选到低位二进制
若之前的高位二进制都和n相同,说明还未分出大小,所以这一位二进制至少不能比n的这一位大
这也就是限制因素了 
*/
int run(int n)
{
	memset(a,0,sizeof(a));
	while( n )
	{
		a[++a[0]]=n&1;
		n>>=1; 
	}
	return dfs(a[0],0,0,1,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	cin >> n >> m;
	cout << run(m)-run(n-1);
}