公平数 (35分)
问题背景
如果一个整数的十六进制表示(不含前导0)中,前一半数字之和等于后一半数字之和,我们称它为公平数。
注意,如果该数的十六进制表示中包含奇数个数字,则正中间的数字既不属于前一半,又不属于后一半。
例如在十六进制下1+D=7+7,因此1DE77是公平数。数字E并不参与计算。
再例如,所有单个数字的十六进制数(即0~F)均为公平数,但F0不是(不能把F0补充前导0写成0F0,进而认为它是公平数)。
给出十六进制数 K, X, Y 和十六进制数字集合 S,求区间[X, Y]之内,有多少个公平数满足:
十六进制表达式(不包含前导0)中每个数字均在集合S中
并且为K的倍数
输入格式
输入第一行为数字集S,包含0~9以及大写字母A~F。
每个数字或字母最多出现一次。
第二行包含 3 个十六进制正整数K, X, Y,均不超过 10 个数字(包含0~9以及大写字母A~F,不包含前导 0)。
输出格式
仅一行,包含一个整数,即满足条件的公平数个数。
样例输入
124C
5 100 FFF
样例输出
4
样例解释
只有四个数满足条件:212,424,4C4,C1C。
题解主要代码如下:
char int2char(int a)
{
char b;
if(a<=9&&a>=0)
b=a+'0';
else if(a>=10&&a<=15)
b=a+'A'-10;
else
b=0;
return b;
}
//求十六进制整数字符长度
int length(int i)
{
char buff[11];
sprintf(buff,"%x",i);
string si(buff);
return si.length();
}
//判断十六进制数字的字符是否都在字符集中
bool inset(int i,string charset)
{
int j;
while(i!=0)
{
int num = i%16;
int pos = charset.find(int2char(num));
if(pos==-1)
return false;
i /= 16;
}
return true;
}
//改进版如下,该版本除了判断该16进制数的字符是否为字符集中的外,还会寻找下一个可能的数字
//由于如果某高位不是字符集的字符,那么所有含该高位的数都是不符合的,因此下一个可能的数就是直接将该高位加1得到//的数(低于该位的数归零)
bool inset(long temp,string charset,long& i)
{
int j=0;
int highest=0;
while(temp!=0)
{
++j;
int num = temp%16;
int pos = charset.find(int2char(num));
if(pos==-1)
highest = j;
temp /= 16;
}
if(highest==0)
return true;
else
{
temp = i;
for(j=0;j<highest-1;j++)
{
i = i - temp%16*pow(16,j);
temp /= 16;
}
i = i + pow(16,highest-1);
return false;
}
}
int main(int argc, char* argv[])
{
string charset;
int k,x,y;
cin>>charset;
scanf("%x %x %x",&k,&x,&y);
sort(charset.begin(),charset.end());
int i,j,sum=0;
while(x%k!=0)
++x;
for(i=x;i<=y;i+=k)
{
int temp = i;
if(inset(temp,charset))//改进版改为inset(temp,charset,i)
{
int len = length(temp);
int rsum=0,lsum=0;
if(len!=1)
{
for(j=0;j<len/2;j++)
{
rsum += temp%16;
temp /= 16;
}
if(len%2!=0)
temp /= 16;
for(j=0;j<len/2;j++)
{
lsum += temp%16;
temp /= 16;
}
if(rsum==lsum)
{
sum++;
printf("%x\t",i);
}
}
else
{
sum++;
}
}
//改进版添加才需要如下代码
else
{
while(i%k!=0)
i++;
i -= k;
}
}
cout<<sum<<endl;
return 0;
}