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;
}