首页 > 编程语言 >玩玩算法题——Episode 2

玩玩算法题——Episode 2

时间:2024-01-18 12:12:25浏览次数:24  
标签:Episode int mapping 玩玩 算法 words known table range

Leetcode每日一题:最大字符串匹配数目

题干如下:

给你一个下标从 0 开始的数组 words ,数组中包含互不相同的字符串。

如果字符串 words[i] 与字符串 words[j] 满足以下条件,我们称它们可以匹配:

字符串 words[i] 等于 words[j] 的反转字符串。
0 <= i < j < words.length
请你返回数组 words 中的 最大 匹配数目。

注意,每个字符串最多匹配一次。

示例 1:

输入:words = ["cd","ac","dc","ca","zz"]
输出:2
解释:在此示例中,我们可以通过以下方式匹配 2 对字符串:

  • 我们将第 0 个字符串与第 2 个字符串匹配,因为 word[0] 的反转字符串是 "dc" 并且等于 words[2]。
  • 我们将第 1 个字符串与第 3 个字符串匹配,因为 word[1] 的反转字符串是 "ca" 并且等于 words[3]。
    可以证明最多匹配数目是 2 。
    示例 2:

输入:words = ["ab","ba","cc"]
输出:1
解释:在此示例中,我们可以通过以下方式匹配 1 对字符串:

  • 我们将第 0 个字符串与第 1 个字符串匹配,因为 words[1] 的反转字符串 "ab" 与 words[0] 相等。
    可以证明最多匹配数目是 1 。
    示例 3:

输入:words = ["aa","ab"]
输出:0
解释:这个例子中,无法匹配任何字符串。

提示:

1 <= words.length <= 50
words[i].length == 2
words 包含的字符串互不相同。
words[i] 只包含小写英文字母。


Star Rating: 2.68(个人观点)

SR转换:
Leetcode简单 -> 约等于1~2星难度
Leetcode普通 -> 约等于3~4星难度
Leetcode困难 -> 约等于5~6星难度


首先,仍然有一种很暴力的解法,复杂度是O(n^2),,也就是固定一个字符串,尝试去找另一个字符串。

但是,这种算法的效率是很低的,因此我们需要想一个更好的方法

我们会发现:
(1)由于words不能包含相同的字符串,因此两个字母相同的字符串不能和任何其他字符串匹配。

Leetcode的题比较偏向数据结构的应用,所以我们要尽量往这个方向去思考...不过对于这个题来说仍然想不到任何思路。
但是我们可以先写一下BF的方法:

class Solution(object):
    def maximumNumberOfStringPairs(self, words):
        """
        :type words: List[str]
        :rtype: int
        """
        count = 0
        for i in range (len(words)):
            if words[i][0] == words[i][1]:
                continue
            for j in range (len(words)):
                if words[i][0] == words[j][1] and words[i][1] == words[j][0]:
                    count += 1
        return count // 2 #每一对匹配字符串计算了两次,所以返回的值要除以2

不过...有没有更好的方法呢?
能够正确提交,32ms(35.71% defeated) and 13.21MB(7.14% defeated)

Current: 23pp(New!)
Overall: 124pp(+19pp)

现在我们思考一下,有没有更好的方法呢?

ChatGPT给出了一个能够拿到34pp的方法:
这个问题可以通过使用字典(哈希表)来提高效率。我们可以将每个字符串的反转形式作为键,然后统计每个反转形式的出现次数。在遍历过程中,如果当前字符串的反转形式已经在字典中出现过,则说明可以进行匹配,匹配的次数就是该反转形式在字典中的出现次数。最后取所有匹配次数的和即为最大匹配数目。

以下是相应的代码:(ChatGPT一开始产生的代码是错的,所以我们又稍微改了一点)

from collections import defaultdict

class Solution(object):
    def maximumNumberOfStringPairs(self, words):
        """
        :type words: List[str]
        :rtype: int
        """
        reverse_counts = defaultdict(int)

        for word in words:
            if word[0] != word[1]:
                reverse_form = word[1] + word[0]
                if reverse_counts[reverse_form] == 1:
                    reverse_counts[reverse_form] += 1
                else:
                    reverse_counts[word[0] + word[1]] += 1
        # 这样精心设计以后,一对匹配的字符串,会在reverse_counts中产生一个值为2的键

        max_matches = sum(count for count in reverse_counts.values() if count % 2 == 0) // 2 #找到值为2的键的总个数
        return max_matches

这种方法的时间复杂度为 O(n),其中 n 是字符串数组的长度。我们通过一次遍历统计了每个反转形式的出现次数,然后计算匹配次数的和。因为字典的查找操作是 O(1) 的平均复杂度,所以总体上是线性时间的。

defaultdict 是 Python 中的一个特殊字典,它可以在创建时指定默认值的类型,当访问一个不存在的键时,会返回默认值。在这个问题中,我们使用 defaultdict(int) 创建了一个默认值为 0 的字典,即如果某个键不存在,则返回 0。

在上述代码中,reverse_counts 是一个字典,它的键是字符串的反转形式,值是该反转形式出现的次数。由于我们在创建 defaultdict(int) 时指定了默认值为 0,所以当访问一个不存在的键时,会返回 0。

这样做的好处是,在遍历 words 数组时,我们无需判断每个键是否存在,直接通过 reverse_counts[reverse_form] 就能获取到相应的值,而且不存在的键会自动被初始化为 0。

使用 defaultdict 可以简化代码,使其更加清晰和简洁。

Yes~ 性能有了显著提升 11ms(100.00% Defeated)和11.54MB(100.00% Defeated)

Current: 34pp(+11pp)
Overall: 135pp(+11pp)

Leetcode 1237. 找出给定方程的正整数解

Star Rating: 3.62
题目描述如下:


给你一个函数 f(x, y) 和一个目标结果 z,函数公式未知,请你计算方程 f(x,y) == z 所有可能的正整数数对 x 和 y。满足条件的结果数对可以按任意顺序返回。

尽管函数的具体式子未知,但它是单调递增函数,也就是说:

f(x, y) < f(x + 1, y)
f(x, y) < f(x, y + 1)
函数接口定义如下:

interface CustomFunction {
public:
// Returns some positive integer f(x, y) for two positive integers x and y based on a formula.
int f(int x, int y);
};
你的解决方案将按如下规则进行评判:

判题程序有一个CustomFunction 的 9 种实现组成的列表,以及一种为特定的 z 生成所有有效数对的答案的方法
判题程序接受两个输入function_id(决定使用哪种实现测试你的代码)以及目标结果 z
判题程序将会调用你实现的 findSolution 并将你的结果与答案进行比较。
如果你的结果与答案相符,那么解决方案将被视作正确答案,即 Accepted 。

示例 1:

输入:function_id = 1, z = 5
输出:[[1,4],[2,3],[3,2],[4,1]]
解释:function_id = 1 暗含的函数式子为 f(x, y) = x + y
以下 x 和 y 满足 f(x, y) 等于 5:
x=1, y=4 -> f(1, 4) = 1 + 4 = 5
x=2, y=3 -> f(2, 3) = 2 + 3 = 5
x=3, y=2 -> f(3, 2) = 3 + 2 = 5
x=4, y=1 -> f(4, 1) = 4 + 1 = 5
示例 2:

输入:function_id = 2, z = 5
输出:[[1,5],[5,1]]
解释:function_id = 2 暗含的函数式子为 f(x, y) = x * y
以下 x 和 y 满足 f(x, y) 等于 5:
x=1, y=5 -> f(1, 5) = 1 * 5 = 5
x=5, y=1 -> f(5, 1) = 5 * 1 = 5

提示:

1 <= function_id <= 9
1 <= z <= 100
题目保证 f(x, y) == z 的解处于 1 <= x, y <= 1000 的范围内。
1 <= x, y <= 1000 的前提下,题目保证 f(x, y) 是一个 32 位有符号整数。


"""
   This is the custom function interface.
   You should not implement it, or speculate about its implementation
   class CustomFunction:
       # Returns f(x, y) for any given positive integers x and y.
       # Note that f(x, y) is increasing with respect to both x and y.
       # i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
       def f(self, x, y):
"""

class Solution(object):
    def findSolution(self, customfunction, z):
        """
        :type num: int
        :type z: int
        :rtype: List[List[int]]
        """
        

不过为什么看到这个题的时候,我完全没有思路呢???...

比如说我们的输入z = 6, customfunction是某个未知的运算...
假如说我们先运行customfunction.f(1,1).它在大部分情况等于1,但也有不等于的情况

如果f(1,1)大于z,那么直接返回一个空列表(因为运算函数是递增函数,所以这个时候不可能有solution)
如果f(1,1)小于z???

那么我们分别尝试下f(1,2)和f(2,1)
假如结果是f(1,2)大于z,f(2,1)小于z?
根据结果,我们可以知道f(2,2)大于max(f(1,2),f(2,1)),因此f(2,2)可以不需要尝试了,我们只需要再次尝试f(3,1)就可以了....

如果f(1,2)和f(2,1)都小于z呢?
那么此时,我们显然需要(1,3),(2,2)和(3,1)都试一试!

我们不妨画图表示一下这两种情况:

如果是第一种情况:

4   x   x   x   x
3   x   x   x   x
2   >   x   x   x
1   <   <   !   ?
    1   2   3   4

如果是第二种情况

4   ?   ?   ?   ?
3   !   ?   ?   ?
2   <   !   ?   ?
1   <   <   !   ?
    1   2   3   4

其中"!"代表接下来要计算的f(x,y),而?表示不知道需不需要计算,大于、小于和等于用">","<"和"="表示,"x"表示不需要计算的值。
显然,一个">"或者一个"="会让处于它右上角的部分全部变成"x",而"<"出现的时候,它右边和它上边的值都需要在下一次再次计算(如果没有受到其他">"或者"="的影响。

那么我们的第一个版本的算法也就出现了:——可能比较像动态规划

"""
   This is the custom function interface.
   You should not implement it, or speculate about its implementation
   class CustomFunction:
       # Returns f(x, y) for any given positive integers x and y.
       # Note that f(x, y) is increasing with respect to both x and y.
       # i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
       def f(self, x, y):
"""

class Solution(object):
    def findSolution(self, customfunction, z):
        """
        :type num: int
        :type z: int
        :rtype: List[List[int]]
        """
        result = []
        #初始化一个1000*1000的矩阵(列表),其值全部为"?"
        #计算f(1,1),如果f(1,1)>z,那么直接返回一个空列表
        #如果f(1,1)=z,直接返回[[1,1]]
        #如果f(1,1)<z,将f(1,2)和f(2,1)置为"!"(如果它们不是"x"的话)
        #计算所有的矩阵中"!"出现的f(x,y)
        #把结果和z进行比较,并将"!"赋值为">","<"或者"="
        #如果结果是">",那么将它右边、上边和右上的矩阵值全部置为"x"
        #如果结果是"=",把这组解放在结果上,并将它右边、上边和右上的矩阵值全部置为"x"
        #如果结果是"<",那么将它右边相邻的元素和上边相邻非"x"的元素置为"!"
        #重复上述过程,直到矩阵中没有"?"为止,返回result.

懒得写具体的实现了,所以让ChatGPT生成了一下:
以下是根据你的描述完成的第一个版本的算法。这个算法使用了一个二维矩阵,代表所有可能的 f(x, y) 的计算状态。在计算过程中,通过不断地更新矩阵的值,得到符合条件的解。

class Solution(object):
    def findSolution(self, customfunction, z):
        """
        :type customfunction: CustomFunction
        :type z: int
        :rtype: List[List[int]]
        """
        result = []
        matrix = [['?' for _ in range(1001)] for _ in range(1001)]

        # 计算 f(1, 1)
        f_1_1 = customfunction.f(1, 1)
        if f_1_1 > z:
            return result
        elif f_1_1 == z:
            result.append([1, 1])
            return result
        else:
            matrix[1][1] = '!'
            matrix[1][2] = matrix[2][1] = '!'

        # 计算所有可能的 f(x, y)
        for x in range(1, 1001):
            for y in range(1, 1001):
                if matrix[x][y] == '!':
                    f_x_y = customfunction.f(x, y)
                    if f_x_y == z:
                        result.append([x, y])
                        matrix[x][y] = '='
                        matrix[x+1:][y+1:] = 'x'
                    elif f_x_y > z:
                        matrix[x][y] = '>'
                        matrix[x+1:][y+1:] = 'x'
                    else:
                        matrix[x][y] = '<'
                        matrix[x+1][y] = matrix[x][y+1] = '!'

        return result

这个算法对 f(x, y) 进行了一些预处理,然后通过矩阵的更新,找到所有可能的解。尽管这个算法可能不是最优解,但它是一种直观的解法,可以帮助你更好地理解问题。

这个版本的算法显然是可以通过测试的,不过耗时和空间都很大。
2490ms(12.50%)和20.28MB(12.50%)

Current: 48pp(New!)
Overall: 176pp(+41pp)

那么,还有没有其他的方法可以解决这个问题呢
...我之前说过这个题似乎比较像动态规划...但是我们要如何利用这个特性呢?
...

对于这个问题,我们可以考虑使用二分搜索来加速查找。首先,我们观察到 f(x, y) 是递增函数,因此对于给定的 xyf(x, y) 的值是唯一确定的。

我们可以按照以下步骤进行优化:

  1. 固定一个变量,比如 x,然后在范围 [1, 1000] 内使用二分搜索查找 y,使得 f(x, y) == z
  2. 如果找到了满足条件的 y,将 [x, y] 添加到结果中。
  3. 增加 x 并重复步骤 1。

这样,我们可以通过二分搜索在 y 的范围内快速找到符合条件的解,从而减小时间复杂度。以下是实现这一优化的代码:

class Solution(object):
    def findSolution(self, customfunction, z):
        """
        :type customfunction: CustomFunction
        :type z: int
        :rtype: List[List[int]]
        """
        result = []

        for x in range(1, 1001):
            left, right = 1, 1000

            while left <= right:
                mid = (left + right) // 2
                value = customfunction.f(x, mid)

                if value == z:
                    result.append([x, mid])
                    break
                elif value < z:
                    left = mid + 1
                else:
                    right = mid - 1

        return result

这是Python所写的代码,时间复杂度从O(n^2)减到了O(nlogn),这里ChatGPT利用了对于特定的x来说,使f(x,y)=z的y是唯一的,利用这个特点简化了时间复杂度。

那么,我们能否把这两个特性结合在一起呢?

class Solution(object):
    def findSolution(self, customfunction, z):
        """
        :type customfunction: CustomFunction
        :type z: int
        :rtype: List[List[int]]
        """
        result = []

        x,y,limit_x,limit_y = 1,1,1001,1001 #数据初始化

        while x < limit_x or y < limit_y:
            left, right = 1, limit_y
            if limit_y == 1:
                break
            while left <= right:
                mid = (left + right) // 2
                value = customfunction.f(x, mid)

                if value == z:
                    result.append([x, mid])
                    y = limit_y
                    break
                elif value < z:
                    left = mid + 1
                else:
                    y = limit_y
                    right = mid - 1

            x += 1

        return result

那么这段算法的性能是124ms(25.00%)和13.13MB(12.50%),和之前相比有了显著的提升!
Current: 56pp(+13pp)
Overall: 184pp(+8pp)

不过我为什么感觉还可以优化...我为什么会想到y = limit_y呢?...
后来发现这并不是把它们结合在一起的方法...而是...

class Solution(object):
    def findSolution(self, customfunction, z):
        """
        :type customfunction: CustomFunction
        :type z: int
        :rtype: List[List[int]]
        """
        result = []

        x,y,limit_x,limit_y = 1,1,1001,1001 #数据初始化

        while x < limit_x or y < limit_y:
            left, right = 1, limit_y
            if limit_y == 1:
                break
            while left <= right:
                mid = (left + right) // 2
                value = customfunction.f(x, mid)

                if value == z:
                    result.append([x, mid])
                    y = limit_y #用于快速跳出内层循环
                    limit_y = mid #这样子才对嘛!~ 动态更新limit_y~(利用x遍历递增的性质,之后的寻找只需要在1,limit_y)范围内执行,并采用二分查找提升效率~
                    break
                elif value < z:
                    left = mid + 1
                else:
                    y = limit_y
                    limit_y = mid
                    right = mid - 1

            x += 1

        return result

这段代码取得了显著的时间和空间开销的下降!~
16ms(100.00%)和11.59MB(100.00%)

Current: 79pp(+23pp)
Overall: 207pp(+23pp)

Luogu P1013. 进制位

Star Rating: 3.73


题目描述

著名科学家卢斯为了检查学生对进位制的理解,他给出了如下的一张加法表,表中的字母代表数字。 例如:

\[\def\arraystretch{2} \begin{array}{c||c|c|c|c} \rm + & \kern{.5cm} \rm \mathclap{L} \kern{.5cm} & \kern{.5cm} \rm \mathclap{K} \kern{.5cm} & \kern{.5cm} \rm \mathclap{V} \kern{.5cm} & \kern{.5cm} \rm \mathclap{E} \kern{.5cm} \\ \hline\hline \rm L & \rm L & \rm K & \rm V & \rm E \\ \hline \rm K & \rm K & \rm V & \rm E & \rm \mathclap{KL} \\ \hline \rm V & \rm V & \rm E & \rm \mathclap{KL} & \rm \mathclap{KK} \\ \hline \rm E & \rm E & \rm \mathclap{KL} & \rm \mathclap{KK} & \rm \mathclap{KV} \\ \end{array}\]

其含义为:

\(L+L=L\),\(L+K=K\),\(L+V=V\),\(L+E=E\)

\(K+L=K\),\(K+K=V\),\(K+V=E\),\(K+E=KL\)

\(\cdots\)

\(E+E=KV\)

根据这些规则可推导出:\(L=0\),\(K=1\),\(V=2\),\(E=3\)。

同时可以确定该表表示的是 \(4\) 进制加法。

输入格式

第一行一个整数 \(n\)(\(3\le n\le9\))表示行数。

以下 \(n\) 行,每行包括 \(n\) 个字符串,每个字符串间用空格隔开。)

若记 \(s_{i,j}\) 表示第 \(i\) 行第 \(j\) 个字符串,数据保证 \(s_{1,1}=\texttt +\),\(s_{i,1}=s_{1,i}\),\(|s_{i,1}|=1\),\(s_{i,1}\ne s_{j,1}\) (\(i\ne j\))。

保证至多有一组解。

输出格式

第一行输出各个字母表示什么数,格式如:L=0 K=1 \(\cdots\) 按给出的字母顺序排序。不同字母必须代表不同数字。

第二行输出加法运算是几进制的。

若不可能组成加法表,则应输出 ERROR!

样例 #1

样例输入 #1

5
+ L K V E
L L K V E
K K V E KL
V V E KL KK
E E KL KK KV

样例输出 #1

L=0 K=1 V=2 E=3
4

首先,我们在拿到这个题以后,一定要考虑一下先把数据输入进去

n = int(input())
table = []
for i in range(n):
    line = list(map(str, input().split()))
    teble.append(line)

然后...看不懂了...

假如说表格是这样的:

+   0   1
0   0   1
1   1   10

这就是一个简单的二进制加法表
与此同时我们也可以改变0 和 1的顺序:

+   0   1
1   1   10
0   0   1

这也是一个二进制表格
根据题意可知,\(s_{i,1} \neq s_{j,1}\),所以结果进制数一定大于等于表格大小。(不一定是等于)

如果是三进制表格,那么他会是什么样的呢?

+   2   1   0
0   2   1   0
1   10  2   1
2   11  10  2

或者说,如果0,1,2不是递增或者递减顺序?

+   2   0   1
1   10  1   2
0   2   0   1
2   11  2  10

显然,我们已经找到破局的方法了!
我们需要先找到"0"是多少!---因为0一定在和第一行数字相等的一行,以及和第一列数字相等的一列当中,这一行和这一列相交的地方对应的字母就是"0"!
如果找到了"0",接下来要如何去做呢?
无论是几进制,两个一位数相加的结果不可能超过这个进制数字的"10+10".因此,我们可以去找结尾为"0"对应字母的值,然后断定其前边的数字是"1"!(这也就说明,这个表格右下角的子表中的值如果是两位数,不可能超过10+10!)
如果能确定出"1"是多少,那么如何确定其他的值呢?(如果有的话)
找到"1"以后,我们去找"1"对应的行和"1"对应的列,找到"2"对应的字母值,然后就简单了!固定"1"对应的行,找到"2"对应的列的字母,就能找到"3",....直到"n"对应的列为"10"为止结束循环。
(实际上,我们也可以看出来,如果输入的表格长度是n,那它应该是一个n-1进制运算表!)
思路渐渐清晰,我们也来写一下对应的代码!

s = int(input())
table = []
for i in range(s):
    line = list(map(str, input().split()))
    table.append(line)

dict = {} #存储最后的映射结果

#在table中寻找table[1:][k]与table[1:][0]相等的k和table[l][1:]与table[0][1:]相等的[l],那么table[k][l]对应的字母的映射值为0.

#遍历寻找结尾字符为table[k][l]的长度为2的第一个字符串table[m][n],确定table[m][n][0]对应字母的映射值为1.

#如果进制数为2,即n=3,则返回结果

#否则,找到table[m][n][0]在table[0][1:]对应的位置p和table[m][n][0]在table[1:][0]对应的位置q,找到table[p][q]对应的位置字母的映射值为2

#如果进制数为3,即n=4,则返回结果

#否则,遍历table[1:][p],然后在table[1:][0]中找到table[p][q]对应的位置,确定这个映射值为3,并记录这个值作为下一次在table[1:][0]中寻找的索引,如果还没有确定所有字母,重复上述操作,直到字典长度达到s - 1.

#按照格式输出结果

让ChatGPT生成了一下代码:

n = int(input())
table = []

for i in range(n):
    line = list(map(str, input().split()))
    table.append(line)

def find_mapping(table, char, n, known_mapping):
    for i in range(1, n):
        if table[i][0] == char:
            return i, 0
        if table[0][i] == char:
            return 0, i
    for i in range(1, n):
        for j in range(1, n):
            if table[i][j] == char:
                if table[i][0] in known_mapping:
                    return i, 0
                else:
                    return 0, j

def solve(table):
    n = len(table)
    known_mapping = {}  # 存储映射结果

    # 寻找0的映射
    for i in range(1, n):
        for j in range(1, n):
            if len(table[i][j]) == 2:
                char = table[i][j][1]
                known_mapping[char] = 0
                break

    # 寻找1到n-1的映射
    for value in range(1, n):
        for i in range(1, n):
            for j in range(1, n):
                if len(table[i][j]) == 2 and table[i][j][0] == str(value):
                    char = table[i][j][1]
                    if char not in known_mapping:
                        row, col = find_mapping(table, char, n, known_mapping)
                        known_mapping[char] = value

    return known_mapping

def print_result(known_mapping):
    result_str = ' '.join([f'{char}={value}' for char, value in sorted(known_mapping.items())])
    print(result_str)

def main():
    known_mapping = solve(table)
    if len(known_mapping) != n - 1:
        print('ERROR!')
    else:
        print_result(known_mapping)

if __name__ == "__main__":
    main()

看看能否通过测试吧!
第一次测试发现有三个WE...可能是需要按照表格第一行的顺序输出映射值,字典的顺序可能和表格第一行/第一列顺序不一样...
(因为本题有一个前置条件,行和列出现的顺序是相同的)
..诶?
也许我们可以简化一下代码!

s = int(input())
table = []
for i in range(s):
    line = list(map(str, input().split()))
    table.append(line)

dict = {} #存储最后的映射结果

#在table中寻找table[1:][k]与table[1:][0]相等的k,那么table[k][k]对应的字母的映射值为0.

#遍历寻找结尾字符为table[k][k]的长度为2的第一个字符串table[m][n],确定table[m][n][0]对应字母的映射值为1.

#如果进制数为2,即n=3,则返回结果

#否则,找到table[m][n][0]在table[0][1:]对应的位置p,找到table[p][p]对应的位置字母的映射值为2

#如果进制数为3,即n=4,则返回结果

#否则,遍历table[1:][p],然后在table[1:][0]中找到table[p][q]对应的位置,确定这个映射值为3,并记录这个值作为下一次在table[1:][0]中寻找的索引,如果还没有确定所有字母,重复上述操作,直到字典长度达到s - 1.

#按照表格中第一行的字母顺序输出结果,并输出进制数

对应的代码如下:

s = int(input())
table = []

for i in range(s):
    line = list(map(str, input().split()))
    table.append(line)

#print(table)

def solve(table):
    n = len(table)
    known_mapping = {}  # 存储映射结果

    # 寻找0的映射
    for i in range(1, n):
        #print(f"i={i}")
        if table[i][1:] == table[0][1:]:
            char = table[i][i][0]
            known_mapping[char] = 0
            break

    # 寻找1的映射
    for i in range(1,n):
        for j in range(1,n):
            if len(table[i][j]) == 2:
                known_mapping[table[i][j][0]] = 1
                break
        if len(known_mapping) == 2:
            break
    
    if n == 3:
        return known_mapping

    # 寻找2的映射,初始化一个next_str
    next_str = table[i][j][0]

    for i in range(1,n):
        if table[0][i] == next_str:
            known_mapping[table[i][i]] = 2
            idx_1 = i
            break
    
    if n == 4:
        return known_mapping

    next_str = table[idx_1][idx_1]

    #寻找3到n-2的映射,range的范围是(3,n-1)
    for value in range(3, n-1):
        for i in range(1, n):
            # 在第1行找到"2",..."n-1"对应的索引
            if table[0][i] == next_str:
                known_mapping[table[idx_1][i]] = value
                next_str = table[idx_1][i]
                break

    return known_mapping

def print_result(known_mapping):
    for i in range(1,s):
        print(f"{table[0][i]}={known_mapping[table[0][i]]}",end=" ")
    print()
    print(s - 1)

def main():
    known_mapping = solve(table)
    #print(known_mapping)
    if len(known_mapping) != s - 1:
        print('ERROR!')
    else:
        print_result(known_mapping)

if __name__ == "__main__":
    main()

这段代码拿到了80分的分数,不过为什么会有一个过不去的测试用例呢...?

Current: 48pp(New!)
Overall: 246pp(+39pp)

读取的是M,期望E

可能是对某个异常情况没有正确处理吧...
为了进一步验证算法正确性,我们需要对Table全部检查一遍,如果输出有错误,那么也会返回error!

s = int(input())
table = []

for i in range(s):
    line = list(map(str, input().split()))
    table.append(line)

#print(table)

def solve(table):
    n = len(table)
    known_mapping = {}  # 存储映射结果

    # 寻找0的映射
    for i in range(1, n):
        #print(f"i={i}")
        if table[i][1:] == table[0][1:]:
            char = table[i][i][0]
            known_mapping[char] = 0
            break

    # 寻找1的映射
    for i in range(1,n):
        for j in range(1,n):
            if len(table[i][j]) == 2:
                known_mapping[table[i][j][0]] = 1
                break
        if len(known_mapping) == 2:
            break
    
    if n == 3:
        return known_mapping

    # 寻找2的映射,初始化一个next_str
    next_str = table[i][j][0]

    for i in range(1,n):
        if table[0][i] == next_str:
            known_mapping[table[i][i]] = 2
            idx_1 = i
            break
    
    if n == 4:
        return known_mapping

    next_str = table[idx_1][idx_1]

    #寻找3到n-2的映射,range的范围是(3,n-1)
    for value in range(3, n-1):
        for i in range(1, n):
            # 在第1行找到"2",..."n-1"对应的索引
            if table[0][i] == next_str:
                known_mapping[table[idx_1][i]] = value
                next_str = table[idx_1][i]
                break

    return known_mapping

def print_result(known_mapping):
    for i in range(1,s):
        print(f"{table[0][i]}={known_mapping[table[0][i]]}",end=" ")
    print()
    print(s - 1)

def cv_int(string,known_mapping):
    if len(string) == 1:
        return known_mapping[string]
    else:
        return known_mapping[string[0]] * (s-1) + known_mapping[string[1]]


def main():
    known_mapping = solve(table)
    #print(known_mapping)
    if len(known_mapping) != s - 1:
        print('ERROR!')
    else:
        #检查原来的表格,验证每个值是否成立
        int_table = []
        for i in range(0,s):
            int_table_line = []
            for j in range(0,s):
                if i == 0 and j == 0:
                    int_table_line.append(0)
                else:
                    int_table_line.append(cv_int(table[i][j],known_mapping))
            int_table.append(int_table_line)
        #print(int_table)
        valid = True
        for i in range(1,s):
            for j in range(1,s):
                valid = valid and int_table[i][0] + int_table[0][j] == int_table[i][j]
        if valid:
            print_result(known_mapping)
        else:
            print('ERROR!')

if __name__ == "__main__":
    main()

那么我们也终于通过了这个题目~!

Current:70pp(+22pp)
Overall:267pp(+21pp)

洛谷P1008 三连击

Star Rating:2.64


题目背景

本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。

题目描述

将 \(1, 2, \ldots , 9\) 共 \(9\) 个数分成 \(3\) 组,分别组成 \(3\) 个三位数,且使这 \(3\) 个三位数构成 \(1 : 2 : 3\) 的比例,试求出所有满足条件的 \(3\) 个三位数。

输入格式

输出格式

若干行,每行 \(3\) 个数字。按照每行第 \(1\) 个数字升序排列。

样例 #1

样例输入 #1

样例输出 #1

192 384 576
* * *
...

* * *
(剩余部分不予展示)

总感觉这道题...应该只是考查我们的理解能力吧..
我大概知道是什么意思了...

感觉有一种很快的方法就是把答案直接列出来,然后print > <
不过...我们要如何才能知道所有的答案呢?

三个数是1:2:3,因此第一个数必须是一个小于333的数字;
要同时出现1~9这9个数字,因此第一个数中必须出现三个不同的数字,而且不能出现0
这样就确定了第一个数一定在123~329之间

基于此方法,我们设计了一个初版的基于穷举的程序把它们都找出来

first_int = 123

while first_int <= 329:
    dict = {}
    for char in str(first_int):
        dict[char] = 1
    for char in str(first_int * 2):
        dict[char] = 1
    for char in str(first_int * 3):
        dict[char] = 1
    if len(dict) == 9:
        print(first_int, first_int * 2, first_int * 3)
    first_int += 1

看起来程序输出了正确结果~

192 384 576
219 438 657
267 534 801
273 546 819
327 654 981

一共有5组解!
提交一下试试~不过...发现一个不对的解...因为中间这组把0也映射进去了!
微调了一下!

first_int = 123

while first_int <= 329:
    dict = {}
    for char in str(first_int):
        dict[char] = 1
    for char in str(first_int * 2):
        dict[char] = 1
    for char in str(first_int * 3):
        dict[char] = 1
    if len(dict) == 9 and '0' not in dict: #如果0不在字典中
        print(first_int, first_int * 2, first_int * 3)
    first_int += 1

这样的话就只有4组解了!

192 384 576
219 438 657
273 546 819
327 654 981

Yes, Accepted啦~

Current: 24pp(New!)
Overall: 284pp(+14pp)

标签:Episode,int,mapping,玩玩,算法,words,known,table,range
From: https://www.cnblogs.com/tuffysbase/p/17971581

相关文章

  • 贪心算法-题目1力扣455(简单题)
    力扣455,给小朋友发饼干假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j]>=g[i],我们可以将这个饼干 j 分配......
  • 算法—差分
    1.一维差分(实质:前缀和的逆运算)给区间[l,r]中的每个数加上c:B[l]+=c,B[r+1]-=c上述操作可以免去构造一个新数组,可以直接给差分赋值2.二维差分给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中的所有元素加上C:S[x1,y1]+=c,S[x2+1,y1]-=c,S[x1,y2+1]-=c,......
  • 2024/1/17 算法笔记
    1.欧拉质数筛功能是给一个整数n查找小于等于n的所有质数。最后使用的是prime【i】//功能:查找n内第x个质数。boolisprime[100000010];//isprime[i]=1表示:i是素数intprime[6000010],cnt=0;//prime存质数voidgetprime(intn){//筛到n也就是n以内的质数memset(is......
  • 算法笔记之图论
    打开转盘锁你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字:'0','1','2','3','4','5','6','7','8','9'。每个拨轮可以自由旋转:例如把'9'变为'0','0'变为......
  • 算法笔记
    1.回溯法(Backtracking)应用:组合、排列、子集等组合型问题,0/1背包问题、图的着色问题等。时空复杂度:时空复杂度较高,指数级别。时间复杂度:O(2^n)或更高,其中n是问题规模。空间复杂度:O(n)或更高,取决于递归深度。特性:通过深度优先搜索遍历解空间。需要撤销选择,回溯到上一步......
  • 学习笔记——ST算法
    ST算法ST算法是一种运用倍增来解决RMQ问题也就是区间最值问题的算法。给定一个长度为\(N\)的序列\(A\),ST算法能在\(\mathcalO(NlogN)\)的时间预处理后,以\(\mathcalO(1)\)的时间在线回答区间最值问题。设\(F_{i,j}\)表示序列\(A\)中下标在子区间\(\left[i,......
  • 算法—前缀和
    1.一维前缀和S[i]=a[1]+a[2]+...a[i]//求s[n]a[l]+...+a[r]=S[r]-S[l-1]//求l-r的序列和2.二维前缀和S[i,j]=s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];第i行j列格子左上部分所有元素的和以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的和为:S[......
  • 代码随想录算法训练营第一天| LeetCode704 二分查找,LeetCode35,LeetCode34,leetcode27.
    LeetCode704题目链接:704.二分查找-力扣(LeetCode)第一时间的想法:简单来说,二分法给我的印象就是想一条绳子上打很多的结,每次对折正好是一个结点,我们需要找到想要的结点比如(a)代码思路就是不断对折一直到绳子两端重合中间没有结点,最后剩下的就是要找的结点a了。......
  • 算法设计与分析-算法模板(不定期更新)
    算法大纲基础算法:模拟,分治,递归,排序,概率,堆进阶算法:DP,贪心,图论,计算几何,FFT,字符串22级期末考试题型:诚信承诺题、选择题5道、归并思想(175/184)、排序的变形题(155/169)、贪心(170/185)、线性动态规划(77/136)、区间动态规划(33/61)、最短路变形(3/25)、多源多汇最大流(12/23),之后......
  • 基础算法(三)二分查找---以“数的三次方”为例
    数的三次方根给定一个浮点数 n,求它的三次方根。输入格式共一行,包含一个浮点数 n。输出格式共一行,包含一个浮点数,表示问题的解。注意,结果保留 6 位小数。数据范围−10000≤n≤10000输入样例:1000.00输出样例:10.000000题解如下#include<iostream>usingnamespace......