看题意第一时间想到使用动态规划来解:遍历每一个下标的值,统计移动到该下标的金币总和最大值。

因为每轮次给定4张卡值分别是1、2、3、4,且必须用完才可以获取下一轮的卡,那么在动规的维度就需要考虑下标与剩余卡值,比如下标为3的点,可以是直从0点使用3号卡0->3,可以是从0点使用1号卡再使用2号卡0->1->3,所以只有相同下标与相同剩余卡值的才能进行比较,为了方便比较,这里使用二进制作为卡值表示0001、0010、0100、1000分别表示1、2、3、4号卡,每次使用以后与取反值进相与。同时为了方便查找,这里使用map作为dp容器。

遍历到的每个点,其下一步的移动目标都会有多种可能,所以需要把每一种可能都枚举出来,并计算由移动到该点的金币最大值f(n),因为到达n点具有最多4种情况(实际上可能会少于4种),所以是取f(n-1)、f(n-2)、f(n-3)、f(n-4)的最大值与该点金币值G(n)的和值。

f(n) =G(n)+ max(f(n-1),f(n-2),f(n-3),f(n-4)),f(n)表示移动到第n个点的金币值总和的最大值,G(n)表示该点的金币值

反向计算不太好想像,这里还是用正向推导,遍历当前点,推导其下一步可能达到的点的最大值,所以起步需要从0点开始(数组插入前置0值)。最后需要判断中途是否存在无法达到的点(金币值小于0),如果下一个点不可达,那么dp集合中就不会存在这个点的值,因为计算过程中每一个点都可能会跳过,但前置条件是四张卡都必须使用完才可以进入下一轮,那么就是每一轮必须是十个点,那只需要计算每一轮最后一个点是否可达即可。

#include <cstdio>
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int main() {
    int k ;
    cin >> k;
    vector<int> vec;
    int b ;
    while (cin >> b) { // 注意 while 处理多个 case
        vec.push_back(b);
    }
    vec.insert(vec.begin(), 0);
    int n = vec.size();
    int cards = 0b00001111;
    unordered_map<int, unordered_map<int, long long>> m;
    m[0][cards] = 0;
    int next;
    long long value;
    for (int i = 0; i < n; i++) {
        if (m[i].size() <= 0) {
            if (i % 10) {
                continue;
            }
            printf("%lld\n", -1LL);
            return 0;
        }
        for (auto mp : m[i]) {
            if (mp.first & 0b00001000 && i + 4 < n) {
                if ((value = mp.second + vec[i + 4]) >= 0) {
                    next = mp.first & 0b11110111;
                    next = next ? next : cards;
                    m[i + 4][next] = std::max(m[i + 4][next], value);
                }
            }
            if (mp.first & 0b00000100 && i + 3 < n) {
                if ((value = mp.second + vec[i + 3]) >= 0) {
                    next = mp.first & 0b11111011;
                    next = next ? next : cards;
                    m[i + 3][next] = std::max(m[i + 3][next], mp.second + vec[i + 3]);
                }
            }
            if (mp.first & 0b00000010 && i + 2 < n) {
                if ((value = mp.second + vec[i + 2]) >= 0) {
                    next = mp.first & 0b11111101;
                    next = next ? next : cards;
                    m[i + 2][next] = std::max(m[i + 2][next], mp.second + vec[i + 2]);
                }
            }
            if (mp.first & 0b00000001 && i + 1 < n) {
                if ((value = mp.second + vec[i + 1]) >= 0) {
                    next = mp.first & 0b11111110;
                    next = next ? next : cards;
                    m[i + 1][next] = std::max(m[i + 1][next], mp.second + vec[i + 1]);
                }
            }
        }
    }
    long long max = 0;
    for (auto mp : m[n - 1]) {
        max = std::max(max, mp.second);
    }
    printf("%lld", max);
    return 0;
}
// 64 位输出请用 printf("%lld")