【Kickstart】2018 Round E - Milk Tea (与官方解答不一样)

解法

  1. 每一位置0或者置1会获得的投诉数很容易算,就是竖着求和
  2. 若是没有forbidden作法,就是很简单的DP

每一个状态都检索感受挺大的,标准答案的作法是每扩展一位,都挑出投诉数最小的101个方案,由于只会ban掉100个方案,因此这最小的101个里确定有能够用的python

我用了TRIE,把forbidden pattern放到一棵trie里,而后dfs这棵树,好比咱们总共10位,遍历到前缀1001时,发现这个节点只有一个孩子(假设为"1"),也就是说,只有10011后面可能有被ban掉的方案,而10010后面5位能够随咱们发挥。web

用数组f[i]记录从第i位到最后都随意发挥时的最小投诉数,这个DP就能够了
那么当咱们遍历到前缀prefix,且当前前缀产生的投诉数为now,此时要决定第i位:数组

  1. 若是节点有2个孩子,说明第i位选啥均可能被ban,分别进一步,now分别加上两种选择的投诉数
  2. 若是节点只有1个孩子(假设为"1"),说明第i位选"0"时能够直接经过f计算结果,而选"1"的结果须要进一步计算
  3. 若是没有孩子,说明已经遍历完全部的位了,此时的前缀prefix必定是个被ban的方案,返回一个无穷大值INF使得这个值永远不被选上。
# -*- coding:utf-8 -*-

from collections import defaultdict
from functools import reduce

INF = 10**5

def solve(p,A,trie):
    f = [0]*(p+1)
    f[p] = 0
    for i in range(p-1,-1,-1):
        f[i] = f[i+1]+min(A[i][0],A[i][1])
    def dfs(now,i,rt):
        if "0" in rt and "1" in rt:
            return min(dfs(now+A[i][0],i+1,rt["0"]),dfs(now+A[i][1],i+1,rt["1"]))
        if "0" in rt:
            return min(now+A[i][1]+f[i+1],dfs(now+A[i][0],i+1,rt["0"]))
        if "1" in rt:
            return min(now+A[i][0]+f[i+1],dfs(now+A[i][1],i+1,rt["1"]))
        return INF
    return dfs(0,0,trie)

if __name__ == "__main__" :
    Trie = lambda:defaultdict(Trie)
    END = True
    t = int(input())
    for round in range(1,t+1):
        n,m,p = map(int,input().split())
        A = [[0,0] for _ in range(p)]
        trie = Trie()
        for _ in range(n):
            string = input()
            for i,c in enumerate(string):
                if c=='1':
                    A[i][0] += 1
                else:
                    A[i][1] += 1
        for _ in range(m):
            reduce(dict.__getitem__,input(),trie)[END] = 1
        print("Case #%d: %d"%(round,solve(p,A,trie)))