poj 3252 Round Numbers(二进制数位DP)

9 篇文章 3 订阅



Round Numbers
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 12505 Accepted: 4790

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


<span style="color: rgb(204, 0, 0);"><span style="font-size:32px;">题意:算出区间内每个数二进制中0的个数大于等于1的个数的数字有多少个</span></span>
思路:把n换成二进制,对这个2进制进行操作,比如16的二进制是10000,一共5位,这样他后面的4位随意怎么变都会比16小,所以枚举这一个01串就是所有的答案。。。数位注意的是 因为是2进制,所以要有一个数字,必须从第一位是1开始算。。。比如16 是 10000, 枚举到00110,这样会是0比1多,但是这个数实际应该是110.。。。一开始卡在这里。。。其余的就是一般的dp了,用dp[][][]分别记录len,0的数量,1的数量。其实dp数组根dfs传的变量是一样的。。。

注意统计某个数里面某些数字的个数,在dfs时候注意下个数组要清零。。

代码:

<span style="font-size:24px;">#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e2;
int dp[maxn][maxn][maxn], bit[maxn]; //分别表示长度,0的个数,1的个数
int dfs(int len, int zero, int one, int lead, int limit)  //lead表示是否出现过1
{
    if(len < 1) return lead && zero >= one;  //如果出现过1并且0比1多就返回1 加lead是为了预防0
    if(!limit && dp[len][zero][one] != -1) return dp[len][zero][one];
    int ans = 0;
    int last = limit ? bit[len] : 1;
    for(int i = 0; i <= last; i++)
    {
        if(i) ans += dfs(len-1, zero, one+1, 1, limit && i == last); //如果是1那就无所谓了
        if(!i && lead) ans += dfs(len-1, zero+1, one, 1, limit && i == last);//0+之前出现过1
        if(!i && !lead) ans += dfs(len-1, 0, 0, 0, limit && i == last);//这里不加就错了
    } //不是像其他的如果不需要直接continue;这里要重置zero跟one的个数;<span style="white-space:pre">	</span>其余的题都是在dfs开始
    if(!limit) dp[len][zero][one] = ans;//ans = 0重置了,这里一个数结束后,通过这个语句重置
    return ans;<span style="white-space:pre">				</span>//否则会0,1会一直递增的
}
int cal(int n)
{
    int k = 0;
    while(n)
    {
        bit[++k] = n % 2;
        n /= 2;
    }
    memset(dp, -1, sizeof(dp));
    return dfs(k, 0, 0, 0, 1); //一开始是0
}
int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m))
    {
        printf("%d\n", cal(m) - cal(n-1));
    }
    return 0;
}<span style="color:#ff0000;">
</span></span>


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值