题解:洛谷 AT_abc466_e Range Flip

【题目来源】

洛谷:AT_abc466_e [ABC466E] Range Flip

【题目描述】

There are \(N\) cards lined up. The cards are numbered \(1, 2, \ldots, N\).

On the front side of card \(i\), an integer \(A_i\) is written, and on the back side, an integer \(B_i\) is written. Initially, every card is facing front side up.

You can perform the following operation at most \(K\) times.

  • Choose integers \(l\) and \(r\) satisfying \(1 \leq l \leq r \leq N\). For each integer \(i\) satisfying \(l \leq i \leq r\), flip card \(i\). Here, flipping a card means turning the side that was facing down before the operation to face up.

After finishing the operations, find the maximum possible value of the sum of the numbers written on the side of the cards that is facing up.

\(N\) 张卡片排成一排。卡片编号为 \(1, 2, \ldots, N\)

卡片 \(i\) 的正面写着整数 \(A_i\),背面写着整数 \(B_i\)。最初,所有卡片都是正面朝上。

你最多可以执行以下操作 \(K\) 次。

  • 选择满足 \(1 \leq l \leq r \leq N\) 的整数 \(l\)\(r\)。对于每个满足 \(l \leq i \leq r\) 的整数 \(i\),将卡片 \(i\) 翻面。这里,翻面是指将操作前朝下的一面朝上。

操作结束后,求朝上的那一面所写的数字之和的最大可能值。

【输入】

The input is given from Standard Input in the following format:

\(N\) \(K\)
\(A_1\) \(B_1\)
\(A_2\) \(B_2\)
\(\vdots\)
\(A_N\) \(B_N\)

【输出】

Output the answer.

【输入样例】

7 2
2 1
6 9
3 5
9 2
4 8
7 4
5 6

【输出样例】

45

【核心思想】

  1. 问题分析:给定 \(N\) 张卡片,每张卡片正面为 \(A_i\)、背面为 \(B_i\),初始全部正面朝上。最多进行 \(K\) 次区间翻转操作(每次选择区间 \([l,r]\) 将区间内所有卡片翻面),求最终朝上面数字之和的最大值。这是一个线性动态规划问题,核心在于将区间翻转操作转化为卡片状态的连续变化,利用"翻转次数的奇偶性"和"区间操作的连续性"设计状态。

  2. 算法选择

    • 一维线性 DP:设 \(dp[i][j][0/1]\) 表示前 \(i\) 张卡片、使用了 \(j\) 次操作、第 \(i\) 张卡片正面朝上(\(0\))或背面朝上(\(1\))时的最大和
    • 状态转移分析:利用区间翻转的连续性——第 \(i\) 张卡片的状态取决于第 \(i-1\) 张卡片是否处于同一个翻转区间内
  3. 关键步骤

    • 初始化\(dp[i][0][0] = dp[i-1][0][0] + A_i\)\(0\) 次操作时所有卡片正面朝上
    • 状态转移(遍历 \(i\)\(1\)\(N\)\(j\)\(1\)\(\min(i, K)\)):
      • \(i\) 张正面朝上\(dp[i][j][0]\)):
        • 不翻转第 \(i\) 张:第 \(i-1\) 张可以是正面或背面,取最大值 \(\max(dp[i-1][j][0], dp[i-1][j][1]) + A_i\)
      • \(i\) 张背面朝上\(dp[i][j][1]\)):
        • 延续翻转区间:第 \(i-1\) 张也是背面朝上,\(dp[i-1][j][1] + B_i\)
        • 开始新翻转区间:第 \(i-1\) 张正面朝上,消耗一次操作,\(dp[i-1][j-1][0] + B_i\)
        • 取两者最大值
    • 答案提取\(ans = \max_{0 \leq j \leq K}(dp[N][j][0], dp[N][j][1])\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot K)\),双层循环,每层状态转移为 \(O(1)\)
    • 空间复杂度:\(O(N \cdot K)\),三维 DP 数组(可优化为 \(O(K)\) 滚动数组)
  5. 线性 DP 与区间翻转的核心思想

    • 翻转的连续性建模:区间翻转操作意味着被翻转的卡片形成一个或多个连续区间。\(dp[i][j][1]\) 中"延续翻转区间"对应第 \(i\) 张与第 \(i-1\) 张在同一个翻转区间内,"开始新翻转区间"对应第 \(i\) 张是某个新区间的起点
    • 操作次数的精确计数:每次"开始新翻转区间"消耗一次操作次数,而"延续翻转区间"不消耗。这确保了操作次数 \(j\) 的正确统计
    • 状态压缩:只需记录第 \(i\) 张卡片的朝向(正面/背面),因为区间翻转的连续性已经隐含在状态转移中——背面朝上只能从前一张背面延续或前一张正面开始新区间
    • 最优子结构:前 \(i\) 张卡片的最优解只依赖于前 \(i-1\) 张的状态,满足无后效性,适合动态规划
    • 适用于区间操作次数受限、状态具有连续性的序列优化类问题

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;              // 常量:最大卡片数量

int n, k;                          // n: 卡片数量; k: 最多操作次数
int ans;                           // ans: 最大可能和
int a[N];                          // a[i]: 卡片 i 正面的数字
int b[N];                          // b[i]: 卡片 i 背面的数字
int dp[N][15][2];                  // dp[i][j][0/1]: 前 i 张卡片,用了 j 次操作,第 i 张卡片正面(0)/背面(1)朝上的最大和

signed main()
{
    cin >> n >> k;                 // 读入卡片数量和最多操作次数

    for (int i = 1; i <= n; i++)  // 读入每张卡片的正面和背面数字
        cin >> a[i] >> b[i];

    for (int i = 1; i <= n; i++)  // 初始化:0 次操作时所有卡片正面朝上
        dp[i][0][0] = dp[i - 1][0][0] + a[i];

    for (int i = 1; i <= n; i++)  // 枚举每张卡片
    {
        for (int j = 1; j <= min(i, k); j++)  // 枚举操作次数(不超过 i 和 k)
        {
            // 第 i 张正面朝上:第 i-1 张正反面均可,不新增操作
            dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1]) + a[i];

            // 第 i 张背面朝上:第 i-1 张必须正面朝上(开始新翻转区间),或继续翻转区间
            dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0]) + b[i];
        }
    }

    for (int i = 0; i <= k; i++)  // 枚举所有可能的操作次数,取最大值
        ans = max({ans, dp[n][i][0], dp[n][i][1]});

    cout << ans << endl;           // 输出最大可能和

    return 0;
}

【运行结果】

7 2
2 1
6 9
3 5
9 2
4 8
7 4
5 6
45
posted @ 2026-07-13 15:10  团爸讲算法  阅读(22)  评论(0)    收藏  举报