求求你不要再摔馍片了

求求你不要再摔馍片了

拆成两条链,维护起始

一、问题转化

按初始顺序处理每包馍片时,每个馍片只有两种命运:

  • 不扔:留在前面的部分,保持原相对顺序,组成子序列 \(A\)
  • 扔到最后:按被扔的先后顺序排到后面,组成子序列 \(B\)

最终吃的顺序一定是:

\[A + B \]

所以本质上是:把原序列划分成两个保持原序的子序列 \(A\) 和 \(B\),最后按 \(A\) 接 \(B\) 的顺序吃。


二、额外开心度的来源

额外收益只来自相邻两包馍片的味道转移:

\[\text{gain}(x,y) = (y-x+5)\bmod 5 \]

最终序列 \(A+B\) 的额外收益分三块:

  1. \(A\) 内部相邻转移
  2. \(B\) 内部相邻转移
  3. 跨接收益:\(A\) 的最后一包 \(\to\) \(B\) 的第一包

前两块可以在 DP 过程中边放边算,第三块等所有馍片放完后再补。


三、DP 状态

因为最终要拼成 \(A+B\),我们必须知道:

  • \(A\) 的末尾(用于算 \(A\) 内部转移,也用于最后跨接到 \(B\))
  • \(B\) 的开头(用于最后跨接)
  • \(B\) 的末尾(用于算 \(B\) 内部转移)

所以状态设计为:

\[dp[x][y][z] \]

  • \(x\):\(A\) 链末尾味道,\(0\) 表示 \(A\) 为空
  • \(y\):\(B\) 链开头味道,\(0\) 表示 \(B\) 为空
  • \(z\):\(B\) 链末尾味道,\(0\) 表示 \(B\) 为空
  • 值:当前累计的额外开心度(不含最后的跨接收益)

四、转移

处理当前味道为 \(m\) 的馍片时:

1. 放入 \(A\) 链末尾

  • 若 \(A\) 非空,新增收益 \(\text{gain}(x,m)\)
  • 新状态:\(A\) 末尾变成 \(m\)

\[dp[m][y][z] = max(dp[m][y][z], pre + gain(x, m)) \]

2. 放入 \(B\) 链末尾

  • 若 \(B\) 非空,新增收益 \(\text{gain}(z,m)\)
  • 若 \(B\) 原本为空,当前膜片变成 \(B\) 的开头,即 y = m
  • 末尾一定变成 \(m\)

最后处理A B链连接处的收益即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 1e8;
int dp[6][6][6], ndp[6][6][6]; //A链以x结尾,B链以y开头,以z结尾

int gain(int a, int b) {
    return (b - a + 5) % 5;
}

void solve() {
    int n; cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; i ++) cin >> a[i];

    fill(&ndp[0][0][0], &ndp[0][0][0] + 6*6*6, -INF);
    ndp[0][0][0] = 0;

    for(int m : a) {
        fill(&dp[0][0][0], &dp[0][0][0] + 6*6*6, -INF);
        for(int x = 0; x < 6; x ++) {
            for(int y = 0; y < 6; y ++) {
                for(int z = 0; z < 6; z ++) {
                    if(ndp[x][y][z] == -INF) continue;
                    int pre = ndp[x][y][z];
                    //放A
                    int Aadd = (x == 0 ? 0 : gain(x, m));
                    dp[m][y][z] = max(dp[m][y][z], pre + Aadd);
                    //放B
                    int Badd = (z == 0 ? 0 : gain(z, m));
                    int ny = (y == 0 ? m : y);
                    dp[x][ny][m] = max(dp[x][ny][m], pre + Badd);
                }
            }
        }

        swap(dp, ndp);
    }

    int ans = 0;
    for(int x = 0; x < 6; x ++) {
        for(int y = 0; y < 6; y ++) {
            for(int z = 0; z < 6; z ++) {
                if(ndp[x][y][z] == -INF) continue;
                int res = ndp[x][y][z];
                if(x != 0 && y != 0) res += gain(x, y);
                ans = max(ans, res);
            }
        }
    }
    cout << ans + n << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int _ = 1; //cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-10-13 14:36  thenrify  阅读(9)  评论(0)    收藏  举报