版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Leon_liuqinburen/article/details/77483503
题目链接:http://poj.org/problem?id=3252
感谢ECNU_LZJ的博客带来的思路 http://m.blog.csdn.net/ECNU_LZJ/article/details/74330589
数位DP 思路出来了 就直接套模板 不懂模板的可以来这学学http://www.cnblogs.com/zbtrs/p/6106783.html
定义状态dp[pos][one][zero][lead],pos为当前数位, one为前缀中1的个数 , zero为前缀中0的个数, lead,表示当前数位的前缀中有没有1。
注意前导0!假如要计算小于等于12的Round Number个数,12的长度为4,在dfs到3的时候,路径上的数字依次为0011,0的个数大于1的个数,所以3是符合条件的。真的对吗?
实际上前两个0都是前导0,去掉前导0之后,就发现3其实是不合法的了。
所以dfs的时候,维护一个变量lead,表示当前数位的前缀中有没有1。
/**
定义状态dp[pos][one][zero][lead],pos为当前数位, one为前缀中1的个数 , zero为前缀中0的个数, lead,表示当前数位的前缀中有没有1。
注意前导0!假如要计算小于等于12的Round Number个数,12的长度为4,在dfs到3的时候,路径上的数字依次为0011,0的个数大于1的个数,所以3是符合条件的。真的对吗?
实际上前两个0都是前导0,去掉前导0之后,就发现3其实是不合法的了。
所以dfs的时候,维护一个变量lead,表示当前数位的前缀中有没有1。
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define LL long long
#define MEM(a, b) memset(a, b, sizeof(a))
using namespace std;
LL dp[35][35][35][3];
LL l, r;
int shu[35];
LL dfs(int len, int one, int zero, int lead, bool shangxian)
{
if(zero + len < one) return 0; //剪枝
if (len == 0)
return zero >= one; //当每位数字都枚举完(即len=0),并且zero >= one的时候才是有效的
if (!shangxian && dp[len][one][zero][lead] != -1)
return dp[len][one][zero][lead]; //dp数组的内容应和dfs调用参数的内容相同,除了是否达到上限
LL cnt = 0;
int maxx = (shangxian ? shu[len] : 1);
for (int i = 0; i <= maxx; i++)
{
if(i == 0)
{
if(lead) //前缀有1时 当前位的0才有效
cnt += dfs(len - 1, one, zero + 1, lead, shangxian && i == maxx);
else
cnt += dfs(len - 1, one, zero, lead, shangxian && i == maxx);
}
else if(i == 1)
cnt += dfs(len - 1, one + 1, zero, 1, shangxian && i == maxx);
}
if (!shangxian)
dp[len][one][zero][lead] = cnt;
return cnt;
}
LL solve(LL x)
{
int k = 0;
while (x)
{
shu[++k] = x % 2;
x /= 2;
}
return dfs(k, 0, 0, 0, 1);
}
int main()
{
memset(dp, -1, sizeof(dp));
while(scanf("%I64d %I64d", &l, &r) != EOF)
{
printf("%I64d\n", solve(r) - solve(l - 1));
}
return 0;
}