• P1220 关路灯 (区间dp)


    题目链接:传送门

    题目大意:

      总共有N盏灯,老张从点C(1 ≤ C ≤ N)开始关灯(关灯不需要等待时间,C点的灯直接关掉),与此同时灯开始烧电(已知功率Pi)。

      老张每次可以往左走关最近的灯或者往右走关掉最近的灯,耗时等于距离。问最少的烧电量。

      1 ≤ N ≤ 50。

    思路:

    状态:

      f[i][j]表示在已经关掉i,i+1,…,j的灯时已经浪费的电量。

      讨论发现如果要转移状态,还需要加一维表示老站当前的位置:

      f[i][j][0]表示老张在位置i,f[i][j][1]表示老张在位置j;

    初始状态:

      f[C][C][0] = f[C][C][1] = 0;

      老张一开始可以直接关掉C点的灯。

    状态访问顺序:

      更新f[i][j]的时候需要知道f[i+1][j]和f[i][j-1]的状态。

      也就是说从C点开始向两边访问即可。

    状态转移方程:

      f[i][j][0] = max(f[i+1][j][0] + 从点i+1走到点i所花费的时间 * 还没关掉的灯的总功率,

               f[i+1][j][1] + 从点j走到点i所花费的时间 * 还没关掉的灯的总功率)

      f[i][j][1] = max(f[i][j-1][0] + 从点i走到点j所花费的时间 * 还没关掉的灯的总功率,

               f[i][j-1][1] + 从点j-1走到点j所花费的时间 * 还没关掉的灯的总功率)

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MAX_N = 55;
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    
    int N, C;
    int pos[MAX_N];
    ll P[MAX_N], sum[MAX_N], f[MAX_N][MAX_N][2];// 0 表示停在左端,1 表示停在右端
    
    void dp()
    {
        for (int i = C; i >= 1; i--) {
            for (int j = C; j <= N; j++) {
                if (i == C && j == C) {
                    f[i][j][0] = f[i][j][1] = 0;
                    continue;
                }
                // (i+1, j) -> (i, j)
                if (i+1 <= C) {
                    if (f[i][j][0] > f[i+1][j][0] + (pos[i+1] - pos[i]) * (sum[N] - sum[j] + sum[i])) {
                        f[i][j][0] = f[i+1][j][0] + (pos[i+1] - pos[i]) * (sum[N] - sum[j] + sum[i]);
                    }
                    if (f[i][j][0] > f[i+1][j][1] + (pos[j] - pos[i]) * (sum[N] - sum[j] + sum[i])) {
                        f[i][j][0] = f[i+1][j][1] + (pos[j] - pos[i]) * (sum[N] - sum[j] + sum[i]);
                    }
                }
                // (i, j-1) -> (i, j)
                if (j-1 >= C) {
                    if (f[i][j][1] > f[i][j-1][0] + (pos[j] - pos[i]) * (sum[N] - sum[j-1] + sum[i-1])) {
                        f[i][j][1] = f[i][j-1][0] + (pos[j] - pos[i]) * (sum[N] - sum[j-1] + sum[i-1]);
                    }
                    if (f[i][j][1] > f[i][j-1][1] + (pos[j] - pos[j-1]) * (sum[N] - sum[j-1] + sum[i-1])) {
                        f[i][j][1] = f[i][j-1][1] + (pos[j] - pos[j-1]) * (sum[N] - sum[j-1] + sum[i-1]);
                    }
                }
            }
        }
        cout << min(f[1][N][0], f[1][N][1]) << endl;
        return;
    }
    
    int main()
    {
        cin >> N >> C;
        sum[0] = 0;
        for (int i = 1; i <= N; i++) {
            scanf("%d%lld", pos+i, P+i);
            sum[i] = sum[i-1] + P[i];
        }
        for (int i = C; i >= 1; i--) {
            for (int j = C; j <= N; j++) {
                f[i][j][0] = f[i][j][1] = INF;
            }
        }
        dp();
        return 0;
    }
    /*
    4 3
    2 10000
    3 10
    4 100
    5 200
    
    4 3
    2 10000
    3 200
    4 100
    5 10
    */
    View Code
  • 相关阅读:
    [a0004] <创作> 随笔视图索引
    [a0003] <创作> 全局视图索引
    git提交时忽略指定文件
    解决:gradle 前言中不允许有内容
    【LINQ标准查询操作符总结】之聚合操符
    RxJS 中的创建操作符
    项目管理(1) 什么是项目?
    数据库表数据统计及数据表的数据大小统计SQL
    C# 中一些类关系的判定方法
    Windows Azure NotificationHub+Firebase Cloud Message 实现消息推动(付源码)
  • 原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/9807847.html
Copyright © 2020-2023  润新知