爆刷PAT(甲级)——之【1010】 Radix (25)(25 分)——二分+细节

题意:给出两个数,一个数未知进制,一个数已知进制,由数字和字母组成。

会告诉你其中一个数是什么进制,你需要回答,有没有可能存在某个进制,使得第二个数能够在这个进制上,和已知的数大小相等。


难点:给出的两个数字都是在10的10次方,要用long long ,刚开始我觉得不用longlong,后来发现,给的数字不是十进制,那int是无法存下的。

先用枚举写了,结果各种细节错误、代码字母错误这种新手错误之后。

——一个测试点死活过不去。发现是卡数据了,要用二分才行。

写了二分算法之后,发现,就直接WA了!!!连TL都不是。

Debug了一下午,刚刚才发现自己用来Debug的数据都写错了。

心态爆炸,将WA代码贴于此,明日再思考。


【已解决】

终于AC了这道题。说实话,我都找不到BUG,已经无所畏惧了,没想到突然A了,小兴奋,也许这就是计算机专业敲代码带来的快乐吧。

分享一下我遇见的好几个坑,本题要注意的地方,以及个人为什么WA了这么多次的总结:

1、题目给出两个数值串,假设分别是 n1 和 n2, 而且我们假设题目告知的是 n1 是知道真值的。(如果 是 n2 知道真值,就swap一下两个数,保证是 n1 知道真值的 ,方便我们思考、叙述、解决问题) 由于题目说,给出的两个数值串,最长长度10位。那么第一个坑就是真值大小问题——如果数值是10进制,那么输入数据最大10的10次方,但是很显然本题不知道是几进制,如果是十六进制,那真值肯定巨大无比,所以肯定要用 long long 来处理真值!但是,如果真是长度10位,而且是16进制的数据,那肯定long long 都不够,但测试数据没那么复杂,所以用 long long 来处理真值,是会出现溢出的情况的!但符合题意的真值肯定是在 long long 范围里的!所以处理数据的时候要考虑溢出,否则会导致出错。

2、和上面这一点类似,题目说给出的字母都是在0-9和 a-z 之间的,就想当然的认为进制肯定是在 2-36 之间吗? NO! 很可惜,题目没这么说,我也用血玲珑的教训,确定了测试数据里的 进制 是有大于36 的!甚至有大于100的。 这也是为什么第一份代码只拿了24分的问题。因为某个测试点的超大进制(测试点7)用枚举,是会超时的,所以在这里,不仅仅要考虑到进制的范围不仅仅大于36,还要考虑用二分的办法去求出这个进制。而进制的大小,最大也肯定小于题目给的已知的数1的真值,也就是 n1+1 ,我们就依次作为二分的初步上界。

PS. 在这之前,我没找到自己WA的原因,网上寻找了一下各位道友的code,有人将二分的初步上界,设定为当前数值串中最大的那个字母的值+1,这绝逼是错的啊。我自己手写的例子都没法过。但是他的代码却确实能AC这题。。。很迷。。。

3、第三点就是我个人原因了。思路我都已经想到了。然鹅为什么WA了这么久就是没法Debug,是因为个人细节错误太多了。。。

比如——判断溢出的时候,我刚开始用的是如果当前值大于 0x3f3f3f3f ,就判断为溢出。   但是很明显,0x3f3f3f3f 仅仅是int的最大值的一半,根本判断不了 long long 长度的溢出啊。。。

于是我改成了 0x3f3f3f3f3f3f3f3f  ,又WA 了,这次是因为,我是先判断当前值<0x3f3f3f3f3f3f3f3f  ,大于就溢出;小于就没有溢出,没有溢出就继续乘 进制radix 。——然鹅,明明是应该乘完 radix以后判断有没有溢出好不好。。。也就是顺序放早了。。。

于是我改过来之后,发现还是不对。因为程序溢出的时候,已经变成负数了!怎么可能比0x3f3f3f3f3f3f3f3f   大啊!!好蠢啊自己,被自己蠢哭╥﹏╥ ,于是我的溢出改成—— <0就是溢出 ,就可以了AC 了。。。是不是很蠢。。。

4、还有一点,应该比较简单。如果是直接二分的话,题目说——“如果有多个可能的进制满足题意,输出最小的进制”,那可能就没办法做到最小。所以写二分的时候要注意这里,要写一个“能找到最小的radix”的二分哦~~~

 

 

给大家留一些测试数据好了~~

aaaaa 699050 1 16
期待输出10

699050 aaaaa 2 16
期待输出10

aaaaa 66666 1 16
期待输出Impossible

66666 aaaaa 2 16
期待输出Impossible

acdac 708012 1 16
期待输出10
708012 acdac 2 16
期待输出10

fffffffff ffffffffff 1 16
期待输出Impossible

 

把自己第一次和AC以后的代码都贴一下好了,留个纪念。。。

 

第一遍交的时候,24分代码

Code:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define inf 12
#define INF 0x3f3f3f3f
#define ll long long
#define loop(x,y,z) for(x=y;x<z;x++)

char str1[inf],str2[inf];//存放两个串
char *s1,*s2;//s1是已知,s2是未知
int len1,len2,r;//r是题目输入的基底数
int  flag;//第二个数字的进制


void Input()
{
    int i,tag;
    scanf("%s%s",str1,str2);
    s1=str1;
    s2=str2;
    scanf("%d%d",&tag,&r);
    if(tag==2)swap(s1,s2);//保证s1的串是已知串
}

ll getDigit(char *s,int radix,int len)//根据进制返回字符串s的十进制真值
{
    int i,j;
    ll sum=0;
    loop(i,0,len)
    {
        sum*=radix;
        char c=s[i];
        int t;
        if(c<='9')t=c-'0';
        else t=c-'a'+10;
        if(t>=radix)return -1;//这个是防止串a却妄图用二进制表示
        sum+=t;
    }
    //printf("%d\n",sum);
    return sum;
}

void Solve()
{
    ll num1;//第一个数的值
    len1=strlen(s1);
    len2=strlen(s2);
    //得到已知数字的真值
    num1=getDigit(s1,r,len1);
    //猜测第二个进制
    int i;
    ll l=2,r=num1+1,m;
    while(l<=r)
    {
        m=l+(r-l)/2;
        int t=getDigit(s2,m,len2);
        if(t<num1)
            l=m+1;
        else if(t>num1)
            r=m-1;
        else
        {
            flag=l;
            break;
        }
    }
}

void Output()
{
    if(flag)printf("%d\n",flag);
    else printf("Impossible\n");
}

int main()
{
    Input();
    Solve();
    Output();
    return 0;
}

 

 

 

最终AC代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define inf 12
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define loop(x,y,z) for(x=y;x<z;x++)

char str1[inf],str2[inf];//存放两个串
char *s1,*s2;//s1是已知,s2是未知
int len1,len2,r;//r是题目输入的基底数
ll num1;//第一个数的值
ll  flag;//第二个数字的进制


void Input()
{
    int i,tag;
    scanf("%s%s",str1,str2);
    s1=str1;
    s2=str2;
    scanf("%d%d",&tag,&r);
    if(tag==2)swap(s1,s2);//保证s1的串是已知串
}

ll getDigit(char *s,ll radix,int len)//根据进制返回字符串s的十进制真值
{
    int i,j;
    ll sum=0;
    loop(i,0,len)
    {
        sum*=radix;
        if(sum<0)return INF;//说明越界了
        char c=s[i];
        int t;
        if(c<='9')t=c-'0';
        else t=c-'a'+10;
        if((ll)t>=radix)return -INF;//这个是防止串a却妄图用二进制表示
        sum+=t;
    }
    return sum;
}

void Solve()
{
    len1=strlen(s1);
    len2=strlen(s2);
    //得到已知数字的真值
    num1=getDigit(s1,r,len1);
    //猜测第二个进制
    int i;
    ll l=2,r=num1,m;
    while(l<=r)
    {
        m=l+(r-l)/2;
        ll t=getDigit(s2,m,len2);
        if(t<num1)
            l=m+1;
        else if(t>=num1)
            r=m-1;
    }
    flag=l;
}

void Output()
{
    if(getDigit(s2,flag,len2)==num1)printf("%lld\n",flag);
    else printf("Impossible\n");
}

int main()
{
    Input();
    Solve();
    Output();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值