[poj3252]Round Numbers_数位dp

Round Numbers poj3252

    题目大意:求一段区间内Round Numbers的个数。

    注释:如果一个数的二进制表示中0的个数不少于1的个数,我们就说这个数是Round Number.给定区间l,r<=$2cdot 10^9$。

      想法:又是一道数位dp裸题。我们先来设状态:dp[i]表示二进制表示下有i为而且第一位是1的Round Number的个数。

        这题的特殊之处在于我们并不需要转移?因为我们可以直接求出任意的dp[i]。显然,我们的i位数的第一位是1,所以,后面0的个数一定不能少于(i-1)/2+1.我们在后面的i-1位当中去组合数即可。

      然后,我们思考数位dp的边界:对于一个数来讲,我们可以将所有的位数小于tot的dp全部加起来(tot是该数的二进制表示下的位数)。然后,我们显然只需要对于这个数的二进制从左到右枚举:弄两个计数器分别记录之前经过了多少个0.对于当前数码,如果是0,略过即可。如果是1,则在它之后所有满足该位是0前面满足的数的Round Number都是满足条件的Round Number。,统计即可。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int C[33][33];
void before_hand()//预处理组合数
{
	C[0][0]=1;
	C[1][0]=1;C[1][1]=1;
	for(int i=2;i<33;i++)
	{
		C[i][0]=1;
		for(int j=1;j<i;j++)
			C[i][j]=C[i-1][j-1]+C[i-1][j];
		C[i][i]=1;
	}
}
int bits[33];
int dispose(int n)
{
	if(n<=1) return 0;
	int tot=0;
	while(n>0)//二进制拆分
	{
		if(n&1) bits[tot++]=1;
		else bits[tot++]=0;
		n>>=1;
	}
	int ans=0;
	for(int i=tot-1;i>0;i--)//将位数小于tot的全部加一起
	{
		if(i%2==0)
			ans+=((1<<(i-1)))/2;
		else
			ans+=((1<<(i-1))-C[i-1][(i-1)/2])/2;
	}
	int count_for_0=0,count_for_1=0;//两个计数器(字面意思)
	for(int i=0;i<tot;i++)
	{
		if(bits[i]==0) count_for_0++;
		else count_for_1++;
	}
	if(count_for_0>=count_for_1) ans++;
	count_for_0=0;
	count_for_1=1;
	for(int i=tot-2;i>=0;i--)
	{
		if(bits[i]==1)
		{
			for(int j=i;j>=0&&j+count_for_0+1>=i-j+count_for_1;j--)
				ans+=C[i][j];//简单点儿来说就是直接统计后面的dp值
			count_for_1++;
		}
		else count_for_0++;//如果是0,就直接增加计数器
	}
	return ans;
}

int main()
{
	before_hand();
	int a,b;
	while(scanf("%d%d",&a,&b)!=EOF)
	{
		printf("%d
",dispose(b)-dispose(a-1));//防止重叠
	}
	return 0;
}

     小结:数位dp的恶心之处就是边界特判,沉下心来写一写,不行就把代码拿去copy算了,毕竟这道题的本质不是代码... ...

原文地址:https://www.cnblogs.com/ShuraK/p/8560401.html