📜  门|门CS 2010 |问题 6(1)

📅  最后修改于: 2023-12-03 14:58:37.142000             🧑  作者: Mango

门|门CS 2010 |问题 6

这个题目是一个经典的动态规划问题,也被称为“钥匙和锁”。

题目描述

有n扇门排成一行,每扇门都可以是开或者关的状态。 您的目标是在尽可能少的“按下”次数之后进入门的末端。 具体地,您可以执行一下两个操作:

  1. 按下门的按钮:如果门是开着的,这将其关闭; 如果门是关着的,这将其打开。
  2. 走到下一个门。

每当您通过第i个门时,贡献会增加Ci,其中Ci是固定给定的整数。 然而,如果您在第i个门按下了按钮,则会收到一笔惩罚Pi,其中Pi也是固定给定的整数。

您的目标是最小化总贡献,同时最小化总惩罚。

解决方案

如果问题只要求最小化总贡献,我们可以使用一个简单的贪心算法,每次选择Ci最小的门即可。

但是,由于我们需要最小化总惩罚,这个问题不再是一个简单的贪心问题。 为了解决这个问题,我们可以使用动态规划。

令F[i][0]表示从第1个门到第i个门,第i个门未被按下的最小代价,F[i][1]表示从第1个门到第i个门,第i个门被按下的最小代价。

考虑第i个门是打开还是关闭状态:

  1. 如果第i个门是打开状态,则有F[i][0] = min(F[i-1][0], F[i-1][1] + Pi) + Ci
  2. 如果第i个门是关闭状态,则有F[i][1] = min(F[i-1][0] + Pi, F[i-1][1]) + Ci

我们需要考虑一个特殊情况,即第一个门的状态。

令G[1][0]表示从第1个门到第1个门,第1个门未被按下的最小代价,G[1][1]表示从第1个门到第1个门,第1个门被按下的最小代价。

考虑第1个门是打开还是关闭状态:

  1. 如果第1个门是打开状态,则有G[1][0] = C1,G[1][1] = P1
  2. 如果第1个门是关闭状态,则有G[1][0] = P1,G[1][1] = C1

综上所述,我们可以得出以下动态规划方程:

  1. F[i][0] = min(F[i-1][0], F[i-1][1] + Pi) + Ci
  2. F[i][1] = min(F[i-1][0] + Pi, F[i-1][1]) + Ci
  3. G[1][0] = C1,G[1][1] = P1
  4. F[1][0] = G[1][0],F[1][1] = G[1][1]

根据该方程,我们可以以O(n)的时间复杂度计算出所有F[i][j]。最后答案为min(F[n][0], F[n][1])。

实现
# -*- coding:utf-8 -*-

def door(n, c, p):
    # 初始化
    f = [[float('inf'), float('inf')] for _ in range(n + 1)]
    g = [[float('inf'), float('inf')] for _ in range(n + 1)]
    g[1][0], g[1][1] = c[1], p[1]
    f[1][0], f[1][1] = g[1][0], g[1][1]

    # DP方程
    for i in range(2, n + 1):
        if i == n:
            f[i][0] = min(f[i - 1][0], f[i - 1][1])
            f[i][1] = float('inf')
        else:
            f[i][0] = min(f[i - 1][0], f[i - 1][1] + p[i]) + c[i]
            f[i][1] = min(f[i - 1][0] + p[i], f[i - 1][1]) + c[i]

        g[i][0] = min(g[i - 1][0] + p[i - 1], g[i - 1][1] + c[i - 1])
        g[i][1] = min(g[i - 1][0] + c[i - 1], g[i - 1][1] + p[i - 1])

    return min(f[n][0], f[n][1])

此函数需要三个参数n,c,p,分别表示门的数量、每扇门开着的时候的代价和按下门按钮的时候的代价。函数会返回按照最优策略进入门的末端所需的最小代价。

总结

这个问题是一个经典的动态规划问题,也被称为“钥匙和锁”。通过本问题,我们学会了用动态规划求解复杂的优化问题。