POJ_3252_Round Number_递推

今天上通选课笑尿了,外教让分组不能说话用纸搭塔。。

题意

定义:一个数字x为 round number 当且仅当 x的二进制形式中0的数量大于等于1的数量。
给两个数字start, finish,问两个数字之间(inclusive)有多少个round number。

输入输出

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

分析

拖了好久 没做的题,今天想了想就想出来啦。
首先很正常的思路,找start 和finish之间的round number数量就等于找小于finish的rn减去小于start的rn。
对于一个数字x的二进制形式,有如下递推:

  1. 开头第一位,必定为1,所有比x二进制位数小的rn都可,递归找(1<<(len-1))-1的rn。
  2. 非第一位,为0,在更高位没有减小的情况下(更高位减小的情况涉及的情况在下一种情况),无法减小此位以减小整个数字,直接循环下一位。
  3. 非第一位,为1,将此位减小为0,此位之后的数字需满足0的数量要求,用组合数求此位之后的数字满足要求的数量。之前所说更高位减小的情况即此。之后循环下一位。

代码实现有点挫,debug了一会。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define mxn 33
int start,finish;
long long c[mxn][mxn];
long long s[mxn][mxn];
void set_c(){
    for(int i=1;i<mxn;++i){
        c[i][0]=1;
        c[i][1]=i;
    }
    for(int i=2;i<mxn;++i)
        for(int j=1;j<=i;++j)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    memset(s,0,sizeof(s));
    for(int i=1;i<mxn;++i)
        for(int j=0;j<mxn;++j)
            for(int k=j;k<=i;++k)
                s[i][j]+=c[i][k];
    s[0][0]=1;
}
long long help(int len,int zero){
    if(zero<0)  zero=0;
    return s[len][zero];
}
long long calc(int tem){
    if(tem==0||tem==1)  return 1;
    long long ret=0;
    int num[40];
    int len=0;
    int zero=0;
    int haha=tem;
    while(tem){
        num[len++]=tem%2;
        if(tem%2==0)    ++zero;
        tem/=2;
    }
    if(zero>=len/2+len%2){
        ++ret;
        //cout<<"0: "<<ret<<" "<<haha<<endl;
    }
    for(int i=0;i<len/2;++i){
        num[i]=num[i]+num[len-1-i];
        num[len-1-i]=num[i]-num[len-1-i];
        num[i]-=num[len-1-i];
    }
    int cnt=0;
    for(int i=0;i<len;++i){
        if(!num[i]){
            ++cnt;
            continue;
        }
        if(!i){
            if(len==1){
                //cout<<"tem "<<haha<<" ret "<<ret<<endl;
                return ret;
            }
            ret+=calc((1<<(len-1))-1);
            //cout<<"1: "<<ret<<" "<<haha<<endl;
        }
        else{
            ret+=help(len-i-1,len/2+len%2-cnt-1);
        //  cout<<len-i-1<<" "<<len/2+len%2-cnt-1<<" "<<help(len-i-1,len/2+len%2-cnt-1)<<endl;
        //  cout<<"2: "<<ret<<" "<<haha<<endl;
        }
    }
    //cout<<"tem "<<haha<<" ret "<<ret<<endl;
    return ret;
}
int main(){
    set_c();
    while(scanf("%d%d",&start,&finish)!=EOF){
        long long ans=calc(finish)-calc(start-1);
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值