-
如果发现有些数位题目需要处理的数据多,而且单个数处理数位计数时会随着数的变大越来越麻烦(通常都这样),就适合这种方法或者这种思想解决问题。
因为通常遇到这方面的题目通常会考察一个连续区间内符合某条件的数,这样可以将单个数的位操作合一,直接看题目条件内所有的数的条件符合情况,即直接处理所有数可能的所有位以计算所有位的数可能出现的情况,在将题目给出的数据或数据范围的边界值对应后得到题目要求的结果。
求不大于n的所有数在2进制小包含K个1的数个数(原题是不大于n的可以用K个不相等的2的幂表示的数)
void init()//预处理dp数组得到int类型所有数
{
dp[0][0]=1;
//dp[i][j]表示所有i位数中包含j个1的数个数
for(int i=1;i<=31;i++)
{
dp[i][0]=dp[i-1][0];
for(int j=1;j<=i;j++)
dp[i][j]=dp[i-1][j]+[i-1][j-1];
//当前位为1或0两种情况
}
}
int st(int n,int k)//求不大于x的数中二进制下包含k个1的数个数
{
int tot=0,ans=0;
for(int i=31;i>0;i--)
{
if(x&(1<<i))//当前位上是1
{
tot++;
if(tot>k) break;
x=x^(1<<i);//当前位置零
}
if(1<<(i-1)<=x)
ans+=dp[i-1][k-tot];
}
if(tot+x==k)/*可以判断x本身是不是符合要求,
这里tot大于k则ans不变,小于k则x必为0,只有
tot等于k时只要x等于0就说明x自身也可满足*/
ans++;
return ans;
}
-
而其实,在从左到右碰到一个“1”后,其实就是看后面的能不能出现k-tot个“1”而与原数后面是什么没关系,因为当前位置零后就与后面没什么关系了。所以直接排列组合也可以的。
这个是二进制下的,而如果其它进制下的,那就把原数在其它进制下的形式左起第一个大于1的数和后面所有数都置为1再做就好了(其实这里可能就不是31了,不过其它进制数位只会更少)。
不过解决问题并不是都要换算二进制解决有些问题在原先(一般10)进制下就能够解决,但基本思路差别不大,都是根据给定的数(或范围的边界值)按位进行dp得到解。