CF82D题解

原题链接:https://codeforces.com/contest/82/problem/D
主题:动态规划
废话:为什么cf上dp题这么喜欢记录路径啊(,又烦又没意义。

状态设计

假设目前进程到第 \(now\) 人,我们不难发现 \(dp_{now}\) 的转移时涉及前方剩余的第 \(prev\) 个人以及 \(now+1\) 个人。但是由于我们不确定前方是否剩余第 \(prev\) 个人,从 \(1-i\) 顺推显然是非常困难的,于是我们从答案采用记忆化搜索逆推。于是我们考虑设计状态 \(dp_{prev,now}\) 表示前方剩余第 \(prev\) 个人时 \(now->n\) 全部离开时的最小时间。
对此有状态转移
$
dp[prev][now] = min(min(dp[prev][now + 2] + max(a[now], a[now + 1]), dp[now + 1][now + 2] + max(a[prev], a[now])),
dp[now][now + 2] + max(a[prev], a[now + 1])); $

分别表示选 \(now,now+1\)\(prev,now\)\(prev,now+1\) 时的转移。初状态 \(dp_{1,2}\)

注意特判区间大小为 \(2\)\(3\) 以及只有 \(1\) 个人的情况即可。
记录状态在转移时打标签即可,具体细节看代码。

#include <bits/stdc++.h>

using namespace std;

inline int read() {
    int flag = 1;
    char cch;
    while (!isdigit(cch = getchar())) if (cch == '-') flag = -1;
    int num = cch - '0';
    while (isdigit(cch = getchar())) num = (num << 3) + (num << 1) + cch - '0';
    return num * flag;
}

inline void write(int num) {
    if (num < 0) putchar('-'), num = -num;
    if (num > 9) write(num / 10);
    putchar(num % 10 + '0');
}

const int N = 1010;
int a[1010], dp[1010][1010], n, flag[1010][1010];

void dfs(int prev, int now) {
    if (dp[prev][now]) return;
    if (now == n - 1) {
        dp[prev][now] = max(max(a[prev], a[now]), a[now + 1]) + min(min(a[prev], a[now]), a[now + 1]);
        return;
    }
    if (now == n) {
        dp[prev][now] = max(a[prev], a[now]);
        return;
    }
    dfs(now, now + 2);
    dfs(prev, now + 2);
    dfs(now + 1, now + 2);
    dp[prev][now] = min(min(dp[prev][now + 2] + max(a[now], a[now + 1]), dp[now + 1][now + 2] + max(a[prev], a[now])),
                        dp[now][now + 2] + max(a[prev], a[now + 1]));
    if (dp[prev][now] == dp[now + 1][now + 2] + max(a[prev], a[now])) flag[prev][now] = 1;
    else if (dp[prev][now] == dp[now][now + 2] + max(a[prev], a[now + 1])) flag[prev][now] = 2;
    else flag[prev][now] = 3;
}

void printPath(int prev, int now) {
    if (now == n - 1) {
        if (a[prev] >= a[now] && a[now + 1] >= a[now]) {
            printf("%d %d\n%d", prev, now + 1, now);
            return;
        }
        if (a[now] >= a[prev] && a[now + 1] >= a[prev]) {
            printf("%d %d\n%d", now, now + 1, prev);
            return;
        }
        if ((a[prev] >= a[now + 1] && a[now] >= a[now + 1])) {
            printf("%d %d\n%d", prev, now, now + 1);
            return;
        }
    }
    if (n == now) {
        printf("%d %d", prev, now);
        return;
    }
    if (flag[prev][now] == 1) {
        printf("%d %d\n", prev, now);
        return printPath(now + 1, now + 2);
    }
    if (flag[prev][now] == 2) {
        printf("%d %d\n", prev, now + 1);
        return printPath(now, now + 2);
    }
    if (flag[prev][now] == 3) {
        printf("%d %d\n", now, now + 1);
        return printPath(prev, now + 2);
    }
}

int main() {
    n = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    if (n == 1) {
        printf("%d\n%d", a[1], 1);
        return 0;
    }
    dfs(1, 2);
    write(dp[1][2]);
    puts("");
    printPath(1, 2);
    return 0;
}
posted @ 2024-03-23 07:48  Jefferyzzzz  阅读(42)  评论(0)    收藏  举报