十二届蓝桥杯第三场模拟赛-ABC(二进制-动态规划)

1. 问题描述:

时间限制: 3.0s 内存限制: 512.0MB 本题总分:25 分
杂货铺老板一共有N件物品,每件物品具有ABC三种属性中的一种或多种。从杂货铺老板处购得一件物品需要支付相应的代价。现在你需要计算出如何购买物品,可以使得ABC三种属性中的每一种都在至少一件购买的物品中出现,并且支付的总代价最小。
【输入格式】
输入第一行包含一个整数N。
以下N行,每行包含一个整数C和一个只包含"ABC"的字符串,代表购得该物品的代价和其具有的属性。
【输出格式】
输出一个整数,代表最小的代价。如果无论如何凑不齐ABC三种属性,输出-1。
【样例输入】
5
10 A
9 BC
11 CA
4 A
5 B
【样例输出】
13
【评测用例规模与约定】
对于50%的评测用例,1 <= N <= 20
对于所有评测用例,1 <= N <= 1000, 1 <= C <= 100000

2. 思路分析:

① 一开始想到的是使用递归的方法,尝试所有可能的拼接方案(对于当前的字符串可以选择要还是不要两种平行状态,两个递归方法就可以解决)但是由于数据量为10^5肯定超时的,感觉这道题目应该是可以使用动态规划的方法解决(暴力枚举的条件下进行优化),但是一开始没有找到一个比较好的办法来表示ABC三种属性,在csdn上看到一个使用二进制来表示ABC三种属性的方法,感觉这位老哥写得太棒了,思路使用的是二进制 + 动态规划的方法,其中二进制中的4/2/1分别表示CBA三种属性,这样就可以将ABC三种属性映射到下标中了,因为使用的是python语言所以可以声明一个长度为8的dp列表(相当于是java或者c、c++的数组),dp[i]表示的是具有i的属性花费的最小代价,一个数字i就是代表当前物品具有的属性,dp[i]的含义确定之后接下来的就好办了,对于当前的物品具有的属性,尝试添加到其他的物品中(循环遍历dp[1]~dp[7]),看属性与价值是否发生变化,如果发现能够构成的价值更小那么就更新添加属性之后的那个位置对应的值,dp状态转移方程也是不难想到的dp[v | j] = c + dp[j](c + dp[k] < dp[v | k]),c表示当前输入物品的价值,v表示当前输入的物品的属性,v | j表示尝试将当前的属性添加到具有j代表属性的那个位置,看是否能够构成代价更小的。

② 我们在遇到这种可以使用暴力枚举的题目如果数据量大的时候需要往动态规划这方面思考,看怎么样才可以进行进一步的优化,明确上一个状态与当前的状态之间可能通过什么样的方式进行推导,很多情况下都是一些组合问题的暴力,可能有多种组合方式可以得到当前的状态,我的目标是通过循环找出最优的那种状态,而且状态转移其实是一个很关键的步骤,大佬的参考博客

3. 代码如下:

import sys
if __name__ == '__main__':
    dp = [sys.maxsize] * 8
    N = int(input())
    for i in range(N):
        # split函数的返回值就是一个列表
        cur = input().split()
        c = int(cur[0])
        v = 0
        # v记录当前物品具有的属性
        for j in cur[1]:
            # 左移这么多位表示在xxx对应的二进制位上将置为1表示存在这个属性,
            v |= 1 << (ord(j) - ord("A"))
        # 上面的循环结束之后v表示的就是当前输入物品具有的所有属性
        # 长度为8的循环是为了尝试将当前的输入物品属性添加到之前的位置看是否能够构成代价更小的
        for k in range(1, 8):
            # v & k != v表示之前已经具有所有的属性这个时候添加进来一点贡献都没有
            # c + dp[k] < dp[v | k]其中v | k表示将当前的物品添加到k这个位置对应的属性中看是否构成价值更小
            # 或运算的意思是添加当前输入物品的属性到之前的物品属性中
            if v & k != v and dp[k] != sys.maxsize and c + dp[k] < dp[v | k]:
                dp[v | k] = c + dp[k]
        # 取出dp[v]与c中的最小值的那个
        dp[v] = min(dp[v], c)
    # 输出最后一个元素表示具有三种属性的物品花费的最小价值
    print(dp[7] if dp[7] != sys.maxsize else -1)

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/115260347