2008年百度之星程序设计大赛初赛第1场 第4题 公平数 解法

 

2008年百度之星程序设计大赛初赛第1场 第4题 解法

 

/*题目描述:-----------------------------------------------------------------------------------------------*/

问题背景

如果一个整数的十六进制表示(不含前导0)中,前一半数字之和等于后一半数字之和,我们称它为公平数。

注意,如果该数的十六进制表示中包含奇数个数字,则正中间的数字既不属于前一半,又不属于后一半。

例如在十六进制下1+D=7+7,因此1DE77是公平数。数字E并不参与计算。

再例如,所有单个数字的十六进制数(即0~F)均为公平数,但F0不是(不能把F0补充前导0写成0F0,进而认为它是公平数)。

给出十六进制数X,Y,K和十六进制数字集合S,求区间[X,Y]之内,有多少个公平数满足:

1、十六进制表达式(不包含前导0)中每个数字均在集合S中

2、并且为K的倍数

输入格式

输入第一行为数字集S,包含0~9以及大写字母A~F。

每个数字或字母最多出现一次。

第二行包含3个十六进制正整数K,X,Y,均不超过10个数字。

输出格式

仅一行,包含一个整数,即满足条件的公平数个数。

样例输入

124C

5100FFF

样例输出

4

样例解释

只有四个数满足条件:212,424,4C4,C1C。

 

 

/*算法描述与分析 -----------------------------------------------------------------------------------------------*/

本题有三种算法:

1、在[X,Y]区间内依次枚举整除K的数,判断其是否是公平数,此算法复杂度为0((Y-X)/K)。

2、根据S枚举生成公平数,判断其是否被K整除。具体方法是,按数字的位数(16进制),将区间划分,并一一计算,其中用到数组nums[76][100000]。

对有i(不妨设i为偶数)个位的所有数,先用S生成一组i/2位的数字x,对于每个x,其各位和为xdigits,其右移(i/2)*4位后,

整除K的余数为xmod,则nums[xdigits][xmod]++;然后再用S生成一组i/2位的数字y,对于每个y,其各位和为ydigits,其整除K的余数为ymod,

则y与nums[ydigits][(K-(y%K))%K]中的数字能组成满足提议的公平数。对于i是奇数的情况,与之类似。此算法的复杂度是O(N^(L/2)),

N是S中数字的个数,L是Y的位数(16进制)。

3、动态规划法。利用方法2中的数组,有一个时间复杂度为O(76*12*L*K)的算法,L为Y的位数,且空间复杂度是O(76*K)。

 

最终本文采用的方法是,结合算法1和2。当K较大时采用算法1、当K较小时采用算法2。测试下,当Y为10位数时,也能在1秒内完成运算。

意外的麻烦:对Long long型的应用不熟悉,导致0xFFFFFFFF以上的数据错误。

 

/*源代码 -------------------------------------------------------------------------------------------------------*/

 

#include <vector>

#include <string>

#include <iostream>

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

using namespace std;

 

static const long long edge=11000000;

int nums[76][100000];

int sumnum(long long x){

int ret=0;

while(x){ret+=x&15;x>>=4;}

return ret;

}

 

/****************************************************************************************/

 

class composite{

protected:

int length;

public:

virtual void init()=0;

virtual bool checkend( int i)=0;

virtual int initI(int i)=0;

virtual int nextI(int i)=0;

virtual int deal()=0;

void setlength(int x){length=x;}

int getlength(){return length;}

int searchcomposit(int alength);

};

 

int composite::searchcomposit(int alength){

int i;

char direction;

setlength(alength);

direction='f';

init();i=length;

while(checkend(i)){

if(i==0){

deal();

i++;direction='b';

}

else{

if(direction=='f') {initI(i);i--;}

else{

if(nextI(i)) {direction='f';i--;}

else i++;

}

}

}

return 1;

}

 

class dealnums:public composite{

protected:

long long num;

long long S,K,low,high,center,high_nums,high_modK;

int countcenter;

virtual int getinitI(int i)=0;

public:

virtual void init(){num=0;}

int initI(int i);

int nextI(int i);

void setcon(int s,int k,int alow,int ahigh, int acenter, int acountcenter){

S=s;K=k;low=alow;high=ahigh;center=acenter;countcenter=acountcenter;

}

};

 

class setnums:public dealnums{

protected:

int getinitI(int i){return (i>=length)?1:0; }

public:

bool checkend(int i){return (i==0)? num<=high : true;}

int deal();

};

 

class getnums:public dealnums{

protected:

long long ans;

int getinitI(int i){return 0;}

public:

void init(){

num=0;

high_nums=sumnum(high);

high_modK = (center==0) ? (high<<(length<<2))%K : (((high<<4)|center)<<(length<<2))%K;

}

bool checkend(int i){return i<=length;}

int deal();

void initans(){ans=0;}

long long getans(){return ans;}

};

 

int dealnums::initI(int i){

long long tt=getinitI(i);

i--;

while(!(S&(1<<tt)))tt++;

num&=~(15<<(i<<2));

num|=tt<<(i<<2);

return 1;

}

int dealnums::nextI(int i){

long long tt;

i--;

tt=(num>>(i<<2))&15;tt++;

for(;tt<16;tt++)

if(S&(1<<tt)) break;

if(tt!=16) {num&=~(15<<(i<<2));num|=tt<<(i<<2);return 1;}

else return false;

}

 

int setnums::deal(){

if(countcenter){

for(int k=0;k<16;k++){

if (!(S&(1<<k))) continue;

if(num<high ? true : k<=center)  nums[sumnum(num)][( ((num<<4)|k) << (length<<2) )%K]++;

}

}

else nums[sumnum(num)][(num<<(length<<2))%K]++;

return 1;

}

 

int getnums::deal(){

ans+=nums[sumnum(num)][(K-(num%K))%K];

if(num>low)

ans-=((sumnum(num)==high_nums) && ((num%K+high_modK)%K==0)) ? 1 : 0;

return 1;

}

 

/****************************************************************************************/

class BSfairnumber{

long long X,Y,K;

int S;

 

int getlength(long long x);

bool checkfairnumber(long long x);

long long countbymodK();

long long countnums(long long x);

long long countbycomposite();

long long countfairnumber();

public:

int doit();

};

 

int BSfairnumber::doit(){

 

//随机生成X,Y,K

srand( 5624 );

int l1,l2;

l1=abs(rand())%9+1;

l2=abs(rand())%9+1;

X=0;

for(int i=0;i<l1;i++){

X|=abs(rand())&15;

X<<=4;

}

Y=0;

for(int i=0;i<l2;i++){

Y|=abs(rand())&15;

Y<<=4;

}

if(X>Y) {X=X^Y;Y=X^Y;X=X^Y;}

K=abs(rand())%100000;

 

//生成S集合

int a[17]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};

for(int i=0;i<16;i++){

if(a[i]==-1) break;

S|=1<<a[i];

}

if(!S) return 0;

cout<<hex<<"X:"<<X<<" Y:"<<Y<<" K:"<<K<<" S:"<<S<<endl<<endl;

//计算

countfairnumber();

return 1;

}

 

long long BSfairnumber::countfairnumber(){

long long ans;

if((Y-X)/K < edge) ans=countbymodK();

else ans=countbycomposite();

return ans;

}

 

int BSfairnumber::getlength(long long x){

int length;

for(length=0;x;x>>=4) length++;

return length;

}

 

bool BSfairnumber::checkfairnumber(long long x){

int i, j, t1;

long long tx;

tx=x;

while(tx){

if((1<<(tx&15))&S) tx>>=4;

else return false;

}

i=getlength(x);

tx=x;

for(j=0,t1=0;j<(i>>1);j++){

t1+=tx&15;tx>>=4;

}

if(i&1==1) tx>>=4;

for(j=0;j<(i>>1);j++){

t1-=tx&15;tx>>=4;

}

return (t1==0);

}

 

long long BSfairnumber::countbymodK(){

long long i,sum=0;

for(i=((X%K==0) ? X : X+K-(X%K));i<=Y;i+=K)

if(checkfairnumber(i)) {cout<<hex<<i<<endl;sum++;}

return sum;

}

 

long long BSfairnumber::countnums(long long x){

int low,high,center,length;

long long tt;

 

if(x<16) return x+1;

tt=x;

length=getlength(tt);

low=tt&((1<<((length>>1)<<2))-1);tt>>=((length>>1)<<2);

if(length&1) {center=tt&15;tt>>=4;}

else center=0;

high=tt;

 

memset(nums,0,sizeof(nums));

setnums a;

getnums b;

a.setcon(S,K,low,high,center,length&1);

a.searchcomposit(length>>1);

b.setcon(S,K,low,high,center,length&1);

b.initans();

b.searchcomposit(length>>1);

return b.getans();

}

 

long long BSfairnumber::countbycomposite(){

long long sum=0;

long long i,tt,smalldigit, bigdigit;

smalldigit=getlength(X);

bigdigit=getlength(Y);

 

sum-=countnums(X);

for(i=smalldigit;i<bigdigit;i++){

tt=1;tt<<=(i<<2);tt--;

sum+=countnums(tt);

}

sum+=countnums(Y);

if(!(X%K))

if(checkfairnumber(X)) sum++;

return sum;

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值