//
// main.cpp
// PAT_1010. Radix
//
// Created by wjq on 17/3/30.
// Copyright © 2017年 wjq. All rights reserved.
//
// N1 N2 tag radix
#include <iostream>
#include <string.h>
#define MIN(a,b) (a)<(b)?(a):(b)
using namespacestd;
int tag;
char N1[15],N2[15],sourceN[15],targetN[15];
long longint radix,sourceNum,low,high,mid;
long longint CharToDecimal(char N[],longlong int jinzhi)
{
longlong int sum=0,n=1;
for(int i=(int)strlen(N)-1;i>=0;i--)
{
if(isdigit(N[i]))
sum+=(N[i]-'0')*n;
elseif(isalpha(N[i]))
sum+=(N[i]-'a'+10)*n;
n*=jinzhi;
if(sum<0)
return -1;
}
return sum;
}
long longint BinarySearch()
{
low=2;
for(int j=0;j<strlen(targetN);j++)
{
longlong int temp=0;
if(isdigit(targetN[j]))
temp=targetN[j]-'0';
else
temp=targetN[j]-'a'+10;
if(temp>low)
low=temp+1;
}
high=sourceNum+1;
while(low<=high)
{
mid=(low+high)/2;
if(CharToDecimal(targetN,mid)==sourceNum)
returnmid;
elseif(CharToDecimal(targetN,mid)<sourceNum&&CharToDecimal(targetN,mid)!=-1)
low=mid+1;
else
high=mid-1;
}
return -1;
}
void Sure()
{
if(tag==1)
{
strcpy(sourceN,N1);
strcpy(targetN,N2);
sourceNum=CharToDecimal(N1,radix);
}
else
{
strcpy(sourceN,N2);
strcpy(targetN,N1);
sourceNum=CharToDecimal(N2,radix);
}
}
int main(int argc,const char * argv[])
{
cin>>N1>>N2>>tag>>radix;
if(strcmp(N1,"1")==0&&strcmp(N2,"1")==0)
cout<<2;
elseif(strcmp(N1,N2)==0)
{
if(strlen(N1)==1)
{
if(tag==1)
{
if(isalpha(N1[0]))
cout<<(MIN(radix,N2[0]-'a'+11));
elsecout<<(MIN(radix,N2[0]-'0'+1));
}
else
{
if(isalpha(N1[0]))
cout<<(MIN(radix,N2[0]-'a'+11));
elsecout<<(MIN(radix,N2[0]-'0'+1));
}
}
elsecout<<radix;
}
else
{
Sure();
if(BinarySearch()==-1)
cout<<"Impossible";
else
cout<<BinarySearch();
}
}
思路:
根据tag确定源字符和目标字符,通过源进制算出源数,再通过枚举目标进制算出目标数,若目标数与源数相同,就输出目标进制.
这题的坑点太多了,开始的时候没有用二分,直接从2开始循环来做,有三个点过不去,查了资料之后.
重新写了一遍代码.
1.用二分法是如何保证找出来的进制是最小的?
明确一点,如果为目标字符是二位以上的,那么不同进制转换出来的目标数一定不相同,且目标数随进制的增大而增大.
如果目标字符只有一位,那就要注意了,转换的时候targetNum=targetN[0]*jinzhi^0=target[0],你会发现目标数不随进制的增大而改变.
举个例子,3 3 1 10,tag=1表示第一个3是10进制的,求第二个3的进制,显而易见,第二个3的进制可以是4,5,6,7,8,9,10,11......
因此必须在二分之前解决这种情况
当然这个例子比较特殊,如果源字符和目标字符不相同,比如 3 5 1 10,那你会发现,无论是几进制,这两个数都不会相同.原理还是这条公式targetNum=targetN[0]*jinzhi^0=target[0].
因此讨论清楚了,区分目标字符数是一位或者二位的来解决.
2.为什么要用long long int ?
题意中说明了 源数中出现的最大字符是z,也就是35,源数最大的位数是10.虽然没有说radix的范围,我们就假设radix只有35
那么我们做个估计,假设源字符是zzzzzzzzzz,大概是 35*35^9=35^10<40^10=4^10*10^10 可以估计源数肯定超过10个0,int是存不下的.
并且,zzzzzzzzzz 10 1 35 在这个例子中,目标数等于目标进制等于源数的大小.因此目标进制和目标数也必须声明为long long int.
3.二分法的上界下界 ?
上界只要比源数大1,那么一定能够满足了.
下界要比目标字符中的任何一位上的数大1.
假设目标字符是 ab1 他不可能是2进制的,因为2进制只有两个数01.因此ab1至少是12进制的.
4.这一点我尚存疑惑.因为不知道超出long long int范围之后sum的值究竟是如何变化的,我不确定返回的sum一定是个负数.
在枚举目标进制计算目标数的时候,目标数的大小有可能超出long long int的范围,这时候返回的sum可能是一个负数,如果你不加判断,那么会导致你返回的值一直比源数小,程序会让low=mid+1,然后继续计算,导致目标数越来越大,我们知道,源数一定是在long long int范围内的,既然目标数在目标进制下已经超出long long int范围了,应该要让high=mid-1来缩小目标数来接近源数才对.
5.边界条件
1 1 2 2 或者1 1 1 2 这种例子,必须输出2.