题解【POJ3252】Round Numbers
Description
The cows, as you know, have no fingers or thumbs and thus are unable to play Scissors, Paper, Stone' (also known as 'Rock, Paper, Scissors', 'Ro, Sham, Bo', and a host of other names) in order to make arbitrary decisions such as who gets to be milked first. They can't even flip a coin because it's so hard to toss using hooves.
They have thus resorted to "round number" matching. The first cow picks an integer less than two billion. The second cow does the same. If the numbers are both "round numbers", the first cow wins,
otherwise the second cow wins.
A positive integer \(N\) is said to be a "round number" if the binary representation of \(N\) has as many or more zeroes than it has ones. For example, the integer 9, when written in binary form, is 1001. 1001 has two zeroes and two ones; thus, 9 is a round number. The integer 26 is 11010 in binary; since it has two zeroes and three ones, it is not a round number.
Obviously, it takes cows a while to convert numbers to binary, so the winner takes a while to determine. Bessie wants to cheat and thinks she can do that if she knows how many "round numbers" are in a given range.
Help her by writing a program that tells how many round numbers appear in the inclusive range given by the input (1 ≤ \(Start\) < \(Finish\) ≤ 2,000,000,000).
Input
Line 1: Two space-separated integers, respectively \(Start\) and \(Finish\).
Output
Line 1: A single integer that is the count of round numbers in the inclusive range \(Start..Finish\)
Sample Input
2 12
Sample Output
6
Source
Solution
简化版题意:求出一个区间[a,b]中有多少个“Round Number”,一个数是“Round Number”当且仅当它的二进制表示法中0的个数>=1的个数,其中\(1 \leqslant A, B \leqslant 2,000,000,000\)。
我们可以根据题意先写一个简单的暴力:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
}
//以上不解释
inline bool pd(int x)//判断一个数是不是"Round Number"
{
int y = 0, z = 0;//y是存二进制数中1的个数,z是存0的个数
while (x > 0)
{
if (x & 1)//如果这一位是0
{
++y;//y就加1
}
else
{
++z;//否则z就加1
}
x = x >> 1;//x除以2
}
return z >= y;//这个语句的意思是:如果z>=y,就返回true,否则返回false。
}
int a, b, sum;//a、b是题目中的意思,sum是答案
int main()
{
a = gi(), b = gi();//输入a、b
for (int i = a; i <= b; i++)//从a到b枚举
{
if (pd(i))//如果i是“Round Number"
{
++sum;//sum就加一
}
}
printf("%d", sum);//最后输出sum
return 0;//结束
}
因为\(1 \leqslant A, B \leqslant 2,000,000,000\),很明显,以上代码小数据能AC,但是大数据会TLE。
因此,我们要使用一个更加高效的算法——数位DP。
什么是数位DP呢?可以参考这篇文章:http://www.cnblogs.com/real-l/p/8540124.html
回到这一题:
我们设Rn[n,m]表示区间[n,m]中Round Number的个数,我们利用前缀和,就有:
Rn[a,b] = Rn[0, b] - Rn[0, a - 1]
记忆化搜索思路:
设dp[a][n0][n1]表示从高往低到达第a位时含有n0个0和n1个1在后面任意填时该状态下的总个数。
注意加一个变量flag来判断是否含有前导0。
直接DP思路:
先预处理出dp[i][j]表示前i位有j个0的方案数,然后从高位数位到低位数位DP。
Code
记忆化搜索:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
}
int dp[35][35][35], wei[35];
int dfs(int a, int n0, int n1, int ddd, int flag)
{
if (a == 0)
{
if (flag == 0)//这里不判断flag==0也可以,判不判断的区别在于是不是把0算上,判断就不把0算上了
{
if (n0 >= n1)
{
return 1;
}
else
{
return 0;
}
}
return 0;
}
if (ddd == 0 && dp[a][n0][n1] != -1)
{
return dp[a][n0][n1];
}
int ed = ddd ? wei[a] : 1, ans = 0, nu0, nu1;
for (int i = 0; i <= ed; i++)
{
if (flag && i == 0)
{
nu0 = nu1 = 0;
}
else
{
if (i == 0)
{
nu0 = n0 + 1, nu1 = n1;
}
else
{
nu0 = n0, nu1 = n1 + 1;
}
}
ans = ans + dfs(a - 1, nu0, nu1, ddd && i == ed, flag && i == 0);
}
if (ddd == 0)
{
dp[a][n0][n1] = ans;
}
return ans;
}
int solve(int x)
{
int tot = 0;
while (x)
{
wei[++tot] = x & 1;
x = x >> 1;
}
return dfs(tot, 0, 0, 1, 1);
}
int a, b;
int main()
{
memset(dp, -1, sizeof(dp));
a = gi(), b = gi();
printf("%d\n", solve(b) - solve(a - 1));
return 0;
}
动态规划代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
}
int dp[40][40];
inline int getans(int x)
{
int t = x, wei[40], len = 0, sum = 0, n0 = 0, n1 = 0;;
while (t)
{
wei[++len] = t & 1;
t = t >> 1;
}
for (int i = len - 1; i >= 1; i--)//这里先把第len位变为0,然后一次枚举最高的位数在第i位
{
for (int j = 0; j <= i - 1; j++)
{
if (j >= i - j)
{
sum = sum + dp[i - 1][j];
}
}
}
n1 = 1;
for (int i = len - 1; i >= 1; i--)//这里是在第len位为1的情况下进行dp
{
if (wei[i] == 1)
{
if (i == 1)
{
if (n0 + 1 >= n1)
{
++sum;
}
}
else
{
for (int j = 0; j <= i - 1; j++)
{
if (j + n0 + 1 >= n1 + i - 1 - j)
{
sum = sum + dp[i - 1][j];
}
}
}
++n1;
}
else
{
++n0;
}
}
return sum;
}
int a, b;
int main()
{
a = gi(), b = gi();
memset(dp, 0, sizeof(dp));
dp[1][1] = 1, dp[1][0] = 1;
for (int i = 1; i <= 32; i++)
{
for (int j = 0; j <= i; j++)
{
dp[i + 1][j] = dp[i + 1][j] + dp[i][j], dp[i + 1][j + 1] = dp[i + 1][j + 1] + dp[i][j];
}
}
printf("%d", getans(b + 1) - getans(a));
return 0;
}
题解【POJ3252】Round Numbers的更多相关文章
- [BZOJ1662][POJ3252]Round Numbers
[POJ3252]Round Numbers 试题描述 The cows, as you know, have no fingers or thumbs and thus are unable to ...
- POJ3252 Round Numbers —— 数位DP
题目链接:http://poj.org/problem?id=3252 Round Numbers Time Limit: 2000MS Memory Limit: 65536K Total Su ...
- poj3252 Round Numbers(数位dp)
题目传送门 Round Numbers Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 16439 Accepted: 6 ...
- poj3252 Round Numbers
Round Numbers Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 7625 Accepted: 2625 Des ...
- poj3252 Round Numbers (数位dp)
Description The cows, as you know, have no fingers or thumbs and thus are unable to play Scissors, P ...
- POJ3252 Round Numbers 题解 数位DP
题目大意: 求区间 \([x,y]\) 范围内有多少数的二进制表示中的'0'的个数 \(\ge\) '1'的个数. 解题思路: 使用 数位DP 解决这个问题. 我们设状态 f[pos][num0][n ...
- POJ3252 Round Numbers 【数位dp】
题目链接 POJ3252 题解 为什么每次写出数位dp都如此兴奋? 因为数位dp太苟了 因为我太弱了 设\(f[i][0|1][cnt1][cnt0]\)表示到二进制第\(i\)位,之前是否达到上界, ...
- POJ3252 Round Numbers(不重复全排列)
题目问区间有多少个数字的二进制0的个数大于等于1的个数. 用数学方法求出0到n区间的合法个数,然后用类似数位DP的统计思想. 我大概是这么求的,确定前缀的0和1,然后后面就是若干个0和若干个1的不重复 ...
- poj3252 Round Numbers[数位DP]
地址 拆成2进制位做dp记搜就行了,带一下前导0,将0和1的个数带到状态里面,每种0和1的个数讨论一下,累加即可. WA记录:line29. #include<iostream> #inc ...
随机推荐
- gulp常用插件之rev-del使用
更多gulp常用插件使用请访问:gulp常用插件汇总 rev-del这是一款从模块(如gulp-rev)生成的修订清单中删除旧的.未使用的指纹文件. 更多使用文档请点击访问rev-del工具官网. 安 ...
- Cobalt Strike配置及简单使用
前言 CS分为客户端与服务端,服务端是一个,客户端可以有多个,非常适合团队协同作战,多个攻击者可以同时连接到一个团队服务器上,共享攻击资源与目标信息和sessions,可模拟APT做模拟对抗,进行内网 ...
- 关于华为高斯数据库 GaussDB 版本及认证体系介绍
目录 你需要知道的 技术有国界 从它的名称说起 你听到过的版本 你听到过的流言蜚语 各个版本的区别 版本未来名称 华为 GaussDB 认证体系介绍 GaussDB 其他资料相关链接 你需要知道的 任 ...
- nginx技术
Nginx 处理高并发,单台服务器存在服务瓶颈 Nginx属于nio ,noblocking Io非阻塞式的 Apache属于Bio,Blocking IO 阻塞式的 安装部分 依赖安装:yum -y ...
- win下删除EFI分区
管理员身份,在cmd终端下,用"diskpart"命令. diskpart ##命令进入Microsoft DiskPart 模式 list disk ##展示磁盘分区列表 sel ...
- 剑指offer 56.删除有序链表中的重复结点
56. 删除有序链表中的重复结点 题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3-> ...
- java序列化与反序列化的使用
个人博客 地址:http://www.wenhaofan.com/article/20180925214701 1.什么是序列化和反序列化 Serialization(序列化)是一种将对象以一连串的字 ...
- 安装多个jdk环境7、8、11等,并且切换默认使用版本
背景 在公司开发时,不同项目往往使用不同的jdk.目前使用最多的应该是JDK1.8,但是有些老项目使用1.7甚至1.6等 或者你想学习JDK最新版本,一些新特新如JDK11,现在最新都有JDK13 1 ...
- python 音频可视化
代码整理好放在 github 上了: https://github.com/darkchii/visualize bilibili 演示视频:https://www.bilibili.com/vide ...
- JavaScript的HelloWorld创建
JavaScript JavaScript(简写为JS)是一门世界上最流行的脚本语言. ECMAScript是JavaScript的一个标准,最新版本到es6版本. 但是开发环境和线上环境存在版本不一 ...