码迷,mamicode.com
首页 > 其他好文 > 详细

poj3252(组合数)

时间:2019-05-19 23:16:57      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:display   转化   algorithm   define   部分   cst   for   one   ref   

题目链接:http://poj.org/problem?id=3252

 

题目意思:给出两个整数s,f,问区间[s,f]中 "round number"的个数。(1<=s<f<=2000000000)

 "round number"定义:二进制中0的个数大于等于1的个数。

代码:

技术图片
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int c[33][33]={0};

void init()//组合数打表
{
    c[0][0]=1;
    for(int i=1;i<33;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
}

int count(int n)//寻找比n小符合条件个数 
{
    int d=0,zero=0,res=0;
    bool m[33]={0};
    while(n)// 转化为二进制,d计算 位数,m[]存每一位是0还是1
    {
        m[++d]=n%2;
        n/=2;
    }
    for(int i=2;i<d;i++)//计算二进制位数小于d的情况,(省略了位数为1,因为1不符合条件)
    {
        for(int j=(i+1)/2;j<i;j++)
            res+=c[i-1][j];
    }
    for(int i=d-1;i>=1;i--)//计算二进制位数等于于d的情况
    {
        if(m[i])
        {
            for(int j=(d+1)/2-(zero+1);j<i;j++)//遍历每种小于的情况 
                res+=c[i-1][j];
        }
        else
            zero++;
    }
    return res;
}

int main()
{
    int s,e;
    init();
    while(cin>>s>>e)
    {
        cout<<count(e+1)-count(s)<<endl;
    }
    return 0;
}
View Code

 

思路:区间问题,容易想到写个函数找到1~n的符合条件的数量,这样用1~f中的个数减去1~s中的个数就是答案。

因为这里写的函数  count(int n)   是找到比n小的 "round number"有多少个。所以是用count(f+1)-count(s),搞清楚区间闭合关系。

 

下面就是找 "round number"的工作了:

我们会发现当数字转为二进制时,位数小于该数的十进制一定比它小(这是肯定的,废话一句),那么可以分成两部分:

一、二进制位数小于时

二、二进制位数相同时

 

情况一:符合条件我们需要以下几点:

1、首位一定要是1,(怎么可能不是。。。)

2、0的个数是位数的一半以上,包括一半。

比如:位数是n时,那我们考虑n-1低位必须取一半及以上((n+1)/2)个0。

选择的方式自然就想到组合数,即从n-1个(除掉首位一定是1)选(n+1)/2 ~ n-1的0有多少种情况。

代码:

void init()//组合数打表 
{
    c[0][0]=1;
    for(int i=1;i<33;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
}

while(n)// 转化为二进制,d计算 位数,m[]存每一位是0还是1 
{
    m[++d]=n%2;
    n/=2;
}

for(int i=2;i<d;i++)//计算二进制位数小于d的情况,(省略了位数为1,因为1不符合条件) 
{
    for(int j=(i+1)/2;j<i;j++)
        res+=c[i-1][j];
}

 

情况二:计算相同位数时,因为函数  count (int n)  计算的是小于n有多少个。

即找出二进制每一位为1时,变成0(即小于它)的情况找出。(当然第一位除外,变成0就不是d位数的二进制了,变成d-1位)

举例:二进制  11011  

1)   确定第二位是1         11011                 10 xxx(一定比它小)   c[3][2]+c[3][3](即剩下不确定的三位要二个0或三个0,这样0的个数才大于1)

2)   确定第四位是1         11011                 1001x       c[1][1]一定要是0才可以

3)   确定第五位是1         11011               已经找不到了(所以说这个函数是找到比11011小的符合条件的个数)

很明显这也是一个组合数,必须确保0比1多的,要用一个变量zero记录确定这位之前0的个数,那么剩下的0的个数为

(n+1)/2-(zero+1)到未确定位数,为什么减(zero+1),是确定这位要变成0.

代码:

for(int i=d-1;i>=1;i--)//计算二进制位数等于于d的情况
    {
        if(m[i])
        {
            for(int j=(d+1)/2-(zero+1);j<i;j++)//遍历每种小于的情况 
                res+=c[i-1][j];
        }
        else
            zero++;
    }

 

 

 

 

poj3252(组合数)

标签:display   转化   algorithm   define   部分   cst   for   one   ref   

原文地址:https://www.cnblogs.com/xiongtao/p/10891382.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!