PAT甲级刷题实录——1010

原题链接

https://pintia.cn/problem-sets/994805342720868352/problems/994805507225665536

思路

这题是到目前为止比较难的一题,评测系统的通过率也只有 0.11。

首先需要理解基本题意。题目的要求是给一个已知进制的数,求能不能找出一个进制使得另一个未知进制的数在该进制下和已知进制的数数值相等。大部分人应该都会想到将两个数的数值都转换为十进制后做比较。

在理解了基本题意之后,做的过程中发现这题还有不少坑。

  1. 进制是没有上限的。不要认为 36 即是最大进制,只是 35 是一位上的最大数字而已。
  2. 因为进制没有上限,所以转换为十进制后的数也可能相当相当大,因此用 int 已经不能满足了,需要使用 long long 类型存储进制和数值。
  3. 不能用顺序遍历一个个尝试可能的进制,这样的话会运行超时,需要使用二分法寻找进制。二分法查找进制的下界是未知进制数的最大数字 +1,因为每一位的数字都不能超过进制数,比如十进制的数字就只能是 0-9,不可能会有 abcd。二分法查找进制的上界是已知进制数的数值,即假设已知进制数为 6,未知进制数有意义的最小值为 10,这时进制刚好是 6,如果进制比 6 还大的话就不可能和 6 相等,而如果未知进制数比 10 还小那就和进制无关了,因为所有进制个位的单位都是 1。
  4. 当寻找的可能进制有多个的时候,需要找到最小的那个,这个也是题目中要求的,不过大部分人可能在忙于解决其他坑的时候就把这个要求给忘了

代码

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

long long transfer(char c);
long long calculate(string n, long long radix);
long long countMax(string n);
long long binary(string n, long long l, long long r, long long tagValue);
int main()
{
    string n1, n2;
    int tag;
    long long radix, tagValue;  //用十进制表示的数值
    long long l = 2, r, mid;
    cin >> n1 >> n2 >> tag >> radix;
    if (tag == 2)
        swap(n1, n2);
    tagValue = calculate(n1, radix);
    l = countMax(n2)+1;
    r = tagValue;
    l = binary(n2, l, r, tagValue);
    if (calculate(n2, l) == tagValue)   //看看是真正找到了最小进制数还是不可能
        cout << l;
    else
        cout << "Impossible";
    return 0;
}

long long transfer(char c)  //字符数字转换器
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'z')
        return c - 'a' + 10;
    return -1;  //输入其他字符,错误
}

long long calculate(string n, long long radix)  //将特定进制下的数转换为十进制的数值
{
    long long decValue = 0;
    long long exp = 1;
    for (int i = n.length() - 1; i >= 0; i--)
    {
        decValue += transfer(n[i]) * exp;
        exp *= radix;
        if (decValue < 0 || exp < 0)
            return -1;
    }
    return decValue;
}

long long countMax(string n)    //查找字符串中最大的数字,即最低进制-1
{
    long long max = 0;
    for (int i = 0; i < n.length(); i++)
    {
        if (transfer(n[i]) > max)
            max = transfer(n[i]);
    }
    return max;
}

long long binary(string n2, long long l, long long r, long long tagValue)   //二分法比较
{
    long long mid;
    long long value;
    while (l <= r)
    {
        mid = (l + r) / 2;
        value = calculate(n2, mid);
        if (value >= tagValue || value < 0) //可能进制过大导致value溢出
            r = mid - 1;
        else if (value < tagValue)
            l = mid + 1;
    }
    return l;
}

解释

其他的地方相信大家都能看明白,不过在二分法内部有个需要注意的地方。我把大于和等于写在了一起,并且到最后才输出 l 作为结果。而一般常规的二分法则是等于的时候直接输出结果。这样做的意义在于确保 l 是最小可能的进制数,也就是满足第四个坑的要求,大家可以想一想为什么。当然,返回的 l 也可能是根本没找到后的结果,因此在执行二分法之后还需要加一行语句判断 l 是否是真正的结果。

原文地址:https://www.cnblogs.com/aopstudio/p/12229474.html

时间: 2024-10-08 13:47:22

PAT甲级刷题实录——1010的相关文章

PAT甲级刷题实录——1004

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184 思路 很明显这题需要用到树这个数据结构,问题是怎么来存.一开始我是这样想的:因为它只问了每一层叶子结点数,所以最简单的情况下我只需要两个数据就行,一个是结点的所在的层级,另一个是结点是否含有子结点.所有的结点都存储在vector中,另外创建一个存储每一层叶子结点个数的数组用于最后输出结果.遍历vector,记录每一层不含有子结点的个

PAT甲级刷题实录——1011

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805504927186944 思路 这题就很简单了,每行输入的时候找出最大的记录下来,同时记录下标.输入完毕后根据下标转换成结果(W,T,L)并储存起来,再根据每行的最大值计算profit.最后输出结果和profit即可,代码如下. 代码 #include <iostream> #include <vector> using namespace s

PAT甲级刷题实录——1013

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805500414115840 思路 题目大意是说一些城市之间有路相通,假设其中一个城市被敌方占领了,计算需要新修多少条路才能让剩下的城市全部联通.首先这是一个典型的图论问题,我们可以用邻接矩阵去存城市之间的联通关系.可以用深度遍历的思想去解决这个问题.思路大意如下:建立一个数组存储哪些城市已经被联通,1代表已联通,0代表未联通:定义一个变量记录需要新修的路的数量

PAT甲级刷题实录——1014

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805498207911936 思路 这题需要用到队列,而且不止一条.首先是每个等待窗口各需要一条,另外在黄线外的等待顾客需要一条.C++提供了现成了现成的队列类型,只要引用头文件queue即可. 算法基本运行过程是:在输入顾客等待时间时依次填满每条队列,超出队列容量的,即编号大于N*M+1的顾客,则push进黄线外的等待队列中.当有窗口有顾客处理完毕后,则将该顾

1085. Perfect Sequence (25)-PAT甲级真题

1085. Perfect Sequence (25) 时间限制 300 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CAO, Peng Given a sequence of positive integers and another positive integer p. The sequence is said to be a "perfect sequence" if M <= m * p where M and m

1020. Tree Traversals (25) PAT甲级真题

之前我看了这道题,实在是看不懂网上的解题答案,他们的具体思路基本上就是通过后续遍历和中序遍历,直接推出层次遍历. 我苦思冥想了半天,是在没看懂这种思路,于是想了一个笨点的但是也比较好理解的思路,通过后续和中序,先推出整个二叉树,再考虑 对二叉树层次遍历. 本题还有一点要注意的时在输出结果的末尾,如果使用了类似 pirntf("%d ",data); 这样的格式是不对的,一定要对末尾进行判断消除最尾端的空格. 首先最核心的部分是通过两次遍历反推回二叉树:这里的思路是,后续遍历的最末尾,一

1078. Hashing (25)-PAT甲级真题

1078. Hashing (25)The task of this problem is simple: insert a sequence of distinct positive integers into a hash table, and output the positions of the input numbers. The hash function is defined to be "H(key) = key % TSize" where TSize is the

PAT甲题题解-1111. Online Map (30)-PAT甲级真题(模板题,两次Dijkstra,同时记下最短路径)

题意:给了图,以及s和t,让你求s到t花费的最短路程.最短时间,以及输出对应的路径.   对于最短路程,如果路程一样,输出时间最少的. 对于最短时间,如果时间一样,输出节点数最少的.   如果最短路程和最短时间路径一样,合并输出一次即可. 纯粹就是练习dijkstra,没什么难的. 第一次dijkstra求最短路程,记录下每个节点的路程和时间. 第二次dijkstra求最短时间,记录下每个节点的时间和经过的节点数. pre数组用来存储前驱节点,保存路径 #include <iostream>

PAT甲级水题 A+B in Hogwarts(java string中分隔符的用法)

A+B in Hogwarts 在java.lang包中有String.split()方法,返回是一个数组 我在应用中用到一些,给大家总结一下,仅供大家参考: 1.如果用"."作为分隔的话,必须是如下写法,String.split("\\."),这样才能正确的分隔开,不能用String.split("."); 2.如果用"|"作为分隔的话,必须是如下写法,String.split("\\|"),这样才能正确