题解 | #[CQOI2007]涂色PAINT#

[CQOI2007]涂色PAINT

http://www.nowcoder.com/practice/512619bee5874e85bd2812a0c9066125

思路,本题用动态规划来做,dp[l][r]就是以s[l]和s[r]为结尾的字符串最少的颜色涂法。我们要求的字符串厂度为n,所以s[l]和s[r]之间的距离就为n,问题很复杂。

  • 我们现在来减小问题规模,如果s[l]等于s[r]也就是说字符串两端的的字符相同,这两端的两个字符记录一次涂改。
  • 若不同则在l和r之间我们选取一点K,s[l]和s[r]为结尾的字符串最少的颜色涂法就成了s[l]和s[k], s[k + 1]和s[r] 之间的最少涂法。
  • 在不断切分下,问题最终会变成s[l]等于s[k]等于s[r],即只剩一个字符的情况。此时涂法只有一种。这种情况就是dp需要的初始化。 动态转移方程则可分为两部分,当子字符串s[l:r]的左边s[l]和右边s[r]相等时dp[l][r] = min(dp[l][r - 1], dp[l + 1][r])。就是s[l:r]的最小涂色方案是其子字符串s[l:r - 1]和s[l + 1:r]中所需涂色最小的一种方案。
  • 当子字符串s[l:r]的左边s[l]和右边s[r]不相等时,先继承dp[l][r] = min(dp[l][r - 1], dp[l + 1][r]) + 1作为比较基础。然后又再在l-r之间的寻找子字符串s[l:k]和s[k + 1:r],由于这两个字符串都比s[l:r]小,所以均已经计算并保存在了计算完的dp中,查表确定大小再与继承值比较,找到其中的最小值方程dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r])
  • 最终结果为dp右上角的数值,因为每次循环计算的值都是以对角线顺序填充,最后计算填充的是右上角的值。即s[0:-1]对应的dp[0][-1]填写的值
while True:
    try:
        s = input()
        n = len(s)
        dp = [[0 for _ in range(n)] for _ in range(n)]  # dp[l][r]表示从l字符到r字符涂画所需要的最少次数
        for b in range(n + 1):  # l r之间的相隔步长,从相隔0开始比较,直到相隔n结束
            for l in range(n - b):  # 每次只需要比较到n-b个元素,n - b后面没有步长为b的一对l r
                r = l + b  # 在要比较的l后面相隔b长度提取r
                if b == 0:  # 初始化条件,当1个字符时只有一种涂法
                    dp[l][r] = 1
                elif s[l] == s[r]:  # l和r相等时,dp[l][r]的值是其左边和其下边较小的一个
                    dp[l][r] = min(dp[l + 1][r], dp[l][r - 1])
                else:
                    dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]) + 1  # # l和r不相等时,dp[l][r]的值先继承其左边和其下边较小的一个
                    for k in range(l, r):  # 再在l和r之间寻找一个k,使得dp[l][k] + dp[k + 1][r] 最小
                        dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r])
        print(dp[0][-1])
    except:
        break
全部评论

相关推荐

双非坐过牢:非佬,可以啊10.28笔试,11.06评估11.11,11.12两面,11.19oc➕offer
点赞 评论 收藏
分享
11-01 20:03
已编辑
门头沟学院 算法工程师
Amazarashi66:这种也是幸存者偏差了,拿不到这个价的才是大多数
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务