首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Python中的魔术(Google Code Jam 2014 QR Problem A)

Python中的魔术(Google Code Jam 2014 QR Problem A)
EN

Code Review用户
提问于 2017-02-03 07:10:09
回答 2查看 2.2K关注 0票数 6

我正在通过解决以前的GCJ问题来学习Python。这个问题很容易解决,但我希望对我的代码进行预先检查(更多Pythonic表达式和更高效的算法)。

魔术师首先在方格中排列16张牌:4行牌,每行4张牌。每张卡都有一个从1到16不同的数字,写在显示的一侧。接下来,魔术师请一位志愿者选择一张牌,并告诉他那张牌在哪一行。最后,魔术师再次将16张牌排列在方格格中,可能是以不同的顺序排列。再一次,他问志愿者她的卡在哪一行。只有这两个问题的答案,魔术师才能正确地决定志愿者选择哪一张牌。太棒了,对吧?你决定写一个程序来帮助你理解魔术师的技巧。该程序将给出卡片的两种安排,以及志愿者对这两个问题的回答:第一次安排中所选卡片的行号和第二次排列中所选卡片的行号。从上到下,行号从1到4。您的程序应该确定志愿者选择了哪一张卡;或者如果志愿者可能选择了多张卡(魔术师做得很糟糕);或者如果没有符合志愿者答案的卡片(志愿者作弊)。输入输入的第一行给出测试用例的数量,T测试用例紧随其后。每个测试用例都从包含一个整数的一行开始:第一个问题的答案。接下来的4行表示卡片的第一次排列:每一行包含4个整数,由一个空格分隔。下一行包含第二个问题的答案,下面四行包含相同格式的第二个排列。输出每个测试用例,输出一行包含“case #x: y”的行,其中x是测试用例编号(从1开始)。如果志愿者可以选择一张卡,那么y应该是卡上的号码。如果志愿者可以选择多张牌,Y应该是“坏魔术师!”,没有引号。如果没有符合志愿者答案的卡片,Y应该被“志愿者欺骗!”,没有引号。文本必须是完全正确的,所以考虑从这里复制/粘贴它。样本输入3 2 1 2 3 4 5 6 7 8 9 10 11 12 14 14 15 3 2 2 2 4 3 3 11 11 9 10 7 12 13 14 4 6 9 2 2 3 4 5 6 7 9 10 11 12 14 15 16 2 2 3 4 5 6 7 8 9 10 12 13 14 15 16 2 2 2 3 4 5 6 8 9 10 11 14 15 16 12 14 14 2 4 4 4 5 5 6 8 9 10 12 14 15 16 1 12 14 14 1 4 4 3 4 5 5 9 10 12 14 14 15 16输出案例1:7例2:坏魔术师!案例3:志愿者受骗!

这是我的密码:

代码语言:javascript
运行
复制
import sys
from itertools import repeat


def main():
    submit = False
    filename = "A-small-practice"

    sys.stdin = open(filename + ".in")
    if submit:
        sys.stdout = open(filename + ".out", "w")

    test_cases = int(input())
    main2(test_cases)


def main2(test_cases):
    for cn in range(1, test_cases + 1):
        print "Case #" + str(cn) + ":",
        choices = {}

        # Each test case has 2 answers.
        for i in repeat(None, 2):
            # Volunteer's answer (row # which contains his choice)
            row = int(input())

            # Cards are in 4 rows.
            for r in range(1, 5):
                nums = raw_input()
                if r == row:
                    # A row has 4 cards.
                    for n in nums.split():
                        choices[n] = choices.get(n, 0) + 1

        # The chosen card(s) would be appeared twice.
        # So, pick items in choices whose value(appearance) is 2, and filter
        # others.
        choices = {k: v for k, v in choices.iteritems() if v == 2}

        # Only one choice should exist after filtering.
        if len(choices) == 1:
            print choices.keys()[0]
        elif len(choices) > 1:
            print "Bad magician!"
        else:
            print "Volunteer cheated!"

main()

欢迎任何意见。

EN

回答 2

Code Review用户

回答已采纳

发布于 2017-02-03 08:53:04

  • 命名:main是保留的,所以没关系,但是main2没有任何意义。这个名称应该是函数的一个提示,我会选择guess_card
  • 由于没有提到文件输入,所以我想说的是,要进行标准输入。如果需要,您始终可以读取文件并重定向输入。标准输出也是如此。
  • 你同时使用inputraw_input,为什么?如果您想要进行输入验证,您应该正确地执行它,尝试解析它并最终捕获异常。但是,如果你沿着这条路走下去,那么你也不应该假设其余的输入是正确的。我想说的是,对于这个特定的程序,您可以安全地说,输入的语法会很好。
  • 像这样使用的注释几乎毫无用处,它们应该告诉我们为什么事情是这样发生的,而不是重新描述代码。
  • 您的第二个功能不是模块化的,它将采取所有的情况,并做一切。我认为你应该让这个函数猜出其中的一个例子,然后绕着它,而不是在里面。
  • 没有必要做任何不同于“魔术师”所做的事情,不需要导入迭代工具,只要记住用户答案是一个基于1的索引,而数组是基于0的。

以下是我的看法:

代码语言:javascript
运行
复制
def guess_card(user_rows, card_rows):
    possible_answers = []
    possible_cards = card_rows[user_rows[0] - 1]

    for card in possible_cards:
        if card in card_rows[user_rows[1] + 3]:
            possible_answers.append(card)
            if len(possible_answers) > 1:
                return 'Bad magician!'

    if len(possible_answers) == 0:
        return 'Volunteer cheated!'

    return possible_answers[0]

def main():
    n_test_cases = int(raw_input())
    answers = []

    while n_test_cases:
        n_test_cases -= 1
        user_rows = []
        card_rows = []
        user_rows.append(int(raw_input()))
        for i in range(0, 4):
            card_rows.append(raw_input().split(' '))

        user_rows.append(int(raw_input()))
        for i in range(4, 8):
            card_rows.append(raw_input().split(' '))

        answers.append(guess_card(user_rows, card_rows))

    for answer in answers:
        print answer

if __name__ == "__main__":
    main()

如果您想了解if __name__ == "__main__":行,可以查看这里

票数 11
EN

Code Review用户

发布于 2017-02-03 13:53:59

对文件对象使用with

一般情况下任何与外界联系的事物。with语句会自动处理块末尾这些烦人的对象的清理。

在可能的地方使用列表和字典对象(

)

大多数内置Python函数都适用于这些对象。使用它们可以让您使用更多方便的Python内置程序。

关于这一点:

有关Python特定技术的一些要点:

  • 输入文件(如赋值中所述)使用列表理解很容易地转换为列表。

如:

代码语言:javascript
运行
复制
    file_lines = [i[:-1] for i in open("input.txt")]

您还可以将它与with函数一起使用。立即将文件处理程序对象转换为列表意味着您可以尽快开始使用这些惊人的Python功能。

  • 使用set对象查找匹配

魔术师的戏法是通过在移动卡片之前和之后找到两行共有的卡片。让我们称它们为a和b。一旦将两行作为集合,就很容易找到常见的卡片:

代码语言:javascript
运行
复制
    a.intersection(b)
  • 使用片和split处理字符串输入数据。

做这件事干得好。在for n in nums.split中。事实上,大多数分配都是简单的字符串操作,这实际上不需要在代码中进行大量修饰。

我在下面添加了我对这项任务的简短、简单的处理:

代码语言:javascript
运行
复制
message = ["Bad magician!",0] + ["Volunteer cheated!"]*3

file_lines = [i[:-1] for i in open("input.txt")]

for i in range(int(file_lines[0])):
    offset = (i * 10) + 1
    a = set(file_lines[offset + 0 + int(file_lines[offset + 0])].split(" "))
    b = set(file_lines[offset + 5 + int(file_lines[offset + 5])].split(" "))
    message[1] = a.intersection(b)
    print("Case #" + str(i + 1) + ": " + str(message[len(message[1])]))

(注:它很可能是一个面向对象的版本将是最可重用的,并适用于其他项目。如果你想知道这方面的想法,问我或这里的其他人。)

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/154334

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档