题解:P9676 [ICPC2022 Jinan R] Skills
fishing_cat · · 题解
传送门
思路
动态规划,考虑怎么做。
先想一个最普通的转移,然后看是否可以优化。设
哎?这不看起来没有哪一维可以缩掉吗?确实,那先把最基础的转移写出来看看。
首先考虑,在一项技能从没有被练习时,是不会有熟练度负增长的,自然也就不存在所谓的有多久没练了。所以在转移时,将第二,三维下标为
对于每一次转移,有三种操作。
-
继续练上一次练的
f_{i, j_{next}, k_{next}, h} = \max(f_{i-1, j_{now}, k_{now}, h} + a_{i,h} - j_{next} - k_{next}) -
换练编号下一项
f_{i, k_{next}, 1, h+1} = \max(f_{i-1, j_{now}, k_{now}, h} + a_{i,h+1} - 1 - k_{next}) -
换练编号下下项
f_{i, 1, j_{next}, h+2} = \max(f_{i-1, j_{now}, k_{now}, h} + a_{i,h+2} - j_{next} - 1)
自然,因为只有三项,对
可以想一下,没练的负增长是指数级的,所以第二,三维实际上限不会太大,超出上限后是一定不优的。现在考虑怎样把这个上限求出来。设一次练习加的熟练度是
现在的时间复杂度就是
code
link
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
/*快读省了*/
ll t, n, a[1100][4], ans = -inf;
ll f[3]/* */[214]/* */[214]/* */[4];
// 第几天 下一项没练天数 下下项没练天数 练了哪个
// 0->未被练习过 0->未被练习过
il ll get(ll x) {
if (x > 3) { return x - 3;}
return x;
}
int main() {
read(t);
while (t--) {
read(n);
ans = -1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= 3; j++) {
read(a[i][j]);
ans = max(ans, a[i][j]);
}
}
int MAX = 2*sqrt(ans);// 上限
// init
for (int now = 0; now <= 1; now++)
for (int j = 0; j <= MAX; j++)
for (int k = 0; k <= MAX; k++)
for (int h = 1; h <= 3; h++)
f[now][j][k][h] = 0;
ll now = 1;
for (int i = 1; i <= n; i++) {
ll last = now ^ 1;
// init
for (int j = 0; j <= MAX; j++)
for (int k = 0; k <= MAX; k++)
for (int h = 1; h <= 3; h++)
f[now][j][k][h] = 0;
for (int j = 0; j <= min(MAX, i); j++)
for (int k = 0; k <= min(MAX, i); k++)
for (int h = 1; h <= 3; h++) {
// strat 转移
ll u = j + (j!=0), v = k + (k!=0), LAST = f[last][j][k][h];
f[now][u][v][h] = max(f[now][u][v][h], LAST + a[i][h] - u - v);
/*继续原来的*/
f[now][v][1][get(h+1)] = max(f[now][v][1][get(h+1)], LAST + a[i][get(h+1)] - 1 - v);
/*转下一项*/
f[now][1][u][get(h+2)] = max(f[now][1][u][get(h+2)], LAST + a[i][get(h+2)] - u - 1);
/*转下下项*/
}
now ^= 1;
}
ans = -1;
for (int j = 0; j <= MAX; j++)
for (int k = 0; k <= MAX; k++)
for (int h = 1; h <= 3; h++) {
ans = max(ans, f[now ^ 1][j][k][h]);
}
printf("%lld\n", ans);
}
return 0; // 完结撒花!!!
}