洛谷1220 关路灯

原题链接

很容易看出来是区间\(DP\)(当然爆搜\(+\)玄学剪枝也是可以的)。
\(f[i][j][k]\)表示已经关闭了\([i,j]\)间的路灯,老张的状态为\(k\)时所消耗的最小功耗。

  1. \(k = 0\)时,老张最后关闭了\(i\)灯,即在区间\([i,j]\)的左端。
  2. \(k = 1\)时,老张最后关闭了\(j\)灯,即在区间\([i,j]\)的右端。

\(dis[i]\)表示\(i\)点的位置,\(W[i]\)表示表示\(1\to i\)所有灯的功率和,即前缀和。
则有状态转移方程:

\(\qquad\qquad f[i][j][0] = \min\{ f[i + 1][j][0] + (dis[i + 1] - dis[i]) \times k, f[i + 1][j][1] + (dis[j] - dis[i]) \times k \}, k = W[n] - (W[j] - W[i])\)

\(\qquad\qquad f[i][j][1] = \min\{ f[i][j - 1][0] + (dis[j] - dis[i]) \times k, f[i][j - 1][1] + (dis[j] - dis[j - 1]) \times k \}, k = W[n] - (W[j - 1] - W[i - 1])\)

最后答案就是\(\min\{ f[1][n][0], f[1][n][1] \}\)
另外,因为这题的区间只能从起点开始才有效,所以\(DP\)的右端点循环可以从起点开始,即:
\(\qquad\qquad for\ j = start \to n\)
\(\qquad\qquad\quad for\ i = j - 1 \to 1\)
可以减少点冗余的\(DP\)转移。

#include<cstdio>
#include<cstring>
using namespace std;
const int N = 55;
int f[N][N][2], dis[N], W[N];
inline int re()
{
    int x = 0;
    char c = getchar();
    bool p = 0;
    for (; c < '0' || c > '9'; c = getchar())
        p |= c == '-';
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    return p ? -x : x;
}
inline int minn(int x, int y)
{
    return x < y ? x : y;
}
int main()
{
    int i, j, k, n, m;
    n = re();
    m = re();
    for (i = 1; i <= n; i++)
    {
        dis[i] = re();
        W[i] = re() + W[i - 1];
    }
    memset(f, 60, sizeof(f));
    f[m][m][0] = f[m][m][1] = 0;
    for (j = m; j <= n; j++)
        for (i = j - 1; i; i--)
        {
            k = W[n] - W[j] + W[i];
            f[i][j][0] = minn(f[i + 1][j][0] + (dis[i + 1] - dis[i]) * k, f[i + 1][j][1] + (dis[j] - dis[i]) * k);
            k = W[n] - W[j - 1] + W[i - 1];
            f[i][j][1] = minn(f[i][j - 1][0] + (dis[j] - dis[i]) * k, f[i][j - 1][1] + (dis[j] - dis[j - 1]) * k);
        }
    printf("%d", minn(f[1][n][0], f[1][n][1]));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Iowa-Battleship/p/9835324.html