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;
}

CF82D题解
浙公网安备 33010602011771号