1662: [Usaco2006 Nov]Round Numbers 圆环数
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 270 Solved: 156
[ Submit][ Status][ Discuss]
Description
正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺序。她们甚至也不能通过仍硬币的方式。 所以她们通过"round number"竞赛的方式。第一头牛选取一个整数,小于20亿。第二头牛也这样选取一个整数。如果这两个数都是 "round numbers",那么第一头牛获胜,否则第二头牛获胜。 如果一个正整数N的二进制表示中,0的个数大于或等于1的个数,那么N就被称为 "round number" 。例如,整数9,二进制表示是1001,1001 有两个'0'和两个'1'; 因此,9是一个round number。26 的二进制表示是 11010 ; 由于它有2个'0'和 3个'1',所以它不是round number。 很明显,奶牛们会花费很大精力去转换进制,从而确定谁是胜者。 Bessie 想要作弊,而且认为只要她能够知道在一个指定区间范围内的"round numbers"个数。 帮助她写一个程序,能够告诉她在一个闭区间中有多少Hround numbers。区间是 [start, finish],包含这两个数。 (1 <= Start < Finish <= 2,000,000,000)
Input
* Line 1: 两个用空格分开的整数,分别表示Start 和 Finish。
Output
* Line 1: Start..Finish范围内round numbers的个数
Sample Input
Sample Output
输出解释:
2 10 1x0 + 1x1 ROUND
3 11 0x0 + 2x1 NOT round
4 100 2x0 + 1x1 ROUND
5 101 1x0 + 2x1 NOT round
6 110 1x0 + 2x1 NOT round
7 111 0x0 + 3x1 NOT round
8 1000 3x0 + 1x1 ROUND
9 1001 2x0 + 2x1 ROUND
10 1010 2x0 + 2x1 ROUND
11 1011 1x0 + 3x1 NOT round
12 1100 2x0 + 2x1 ROUND
果然苟蒻就是弱弱弱,一道数位dp困扰了我好久。。。(别问具体时间!!)
嗯。。什么是数位dp?大概就是当某些程序无法使用朴素算法解决的时候将其转换为2进制在2进制数下完成
对于本题,l,r超级大,O(n)算法自然超时。。。
首先本题的结果满足区间减法,设sum[i]为[0,i]的方案数,
则答案为sum[r] - sum[l-1];
设f[len][zero][one]为当前到第len位,放zero个0,one个1的合法方案数
显然这东西是可以记忆化搜索的(建立一棵二进制二叉树,可以发现相同高度的子数方案是相同的)。。。
但是有时候不能!
那什么时候可以??当且仅当当前点不在原数上且它的任意代祖宗至少有一个不是0
而对于当前位放0或放1?
如果当前位置是在原数路径上,那么能放的数取决于原数否则两个都放
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100;
int f[maxn][maxn][maxn],l,r,num[maxn];
int dfs(int len,int zero,int one,bool root,bool line)
{
if (!len) return zero >= one;
if (!root && !line && f[len][zero][one] != -1) return f[len][zero][one];
int last = line?num[len]:1;
int tot = 0;
for (int i = 0; i <= last; i++)
{
if (root)
{
if (!i) tot += dfs(len-1,0,0,1,line && i == num[len]);
else tot += dfs(len-1,zero,one+1,0,line && i == num[len]);
}
else
{
if (!i) tot += dfs(len-1,zero + 1,one,0,line && i == num[len]);
else tot += dfs(len-1,zero,one + 1,0,line && i == num[len]);
}
}
if (!root && !line) f[len][zero][one] = tot;
return tot;
}
int sum(int x)
{
int len = 0;
while (x)
{
num[++len] = x & 1;
x >>= 1;
}
return dfs(len,0,0,1,1);
}
int main()
{
cin >> l >> r;
memset(f,-1,sizeof(f));
cout << sum(r) - sum(l - 1);
return 0;
}
顺便附上一发windows下的对拍。。。
#include<iostream>
#include<cstdio>
using namespace std;
int l,r,i,ans;
int main()
{
cin >> l >> r;
for (i = l; i <= r; i++)
{
int zero,one;
zero = one = 0;
for (int x = i; x; x >>= 1)
{
if (x & 1) ++one;
else ++zero;
}
ans += (zero >= one)?1:0;
}
cout << ans;
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;
int main()
{
srand(time(0));
int l,r;
r = rand() << 7;
l = rand();
printf("%d %d\n",l,r);
return 0;
}
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<windows.h>
using namespace std;
int main()
{
int t = 300;
while (t--)
{
system("data > test.in");
system("1662 < test.in > 1.out");
system("baoli < test.in > 2.out");
if (!system("fc 1.out 2.out")) break;
}
system("pause");
return 0;
}