题解 SP283 【NAPTIME - Naptime】

· · 题解

Naptime | SP283

详情见CSDN博客:【训练题15:线性DP(如何时间复杂度一降再降)】Naptime | SP283

难度

提高+/省选-

独立写出

题意

数据范围

2\le B < N \le 3830 0 \le U_i \le 200000

Sample

五天,睡三个小时

U=2\ 0\ 3\ 1\ 4

则它睡 U_4、U_5、U_6这三个时间段,获得收益为 1*0+4+2

思路

1.【暴力枚举法(没学过DP)】

2.【区间DP / 三维 DP 写法】

3.【线性DP / 二维 DP 单状态,多转移法】

4.【线性DP / 二维 DP 多状态,单转移法】

这样,状态转移方程便可以很好地推出了

真的成功了吗?

想法一:把一天扩展成两天

结果:

想法二:越单纯越好

这样子就成功了!

核心代码

时间复杂度 O(N^2)

Times:70(ms)
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 4e3+50;
const int INF = 0x3f3f3f3f;

int aa[MAX];
int bb[MAX];
int dp[MAX][MAX][4];
int main()
{
    int T;scanf("%d",&T);
    while(T--){
        int N,B;
        scanf("%d%d",&N,&B);
        int mn = INF;
        int id = 0;
        for(int i = 1;i <= N;++i){
            scanf("%d",&bb[i]);
            if(bb[i] < mn){
                mn = bb[i];
                id = i;
            }
        }
        for(int i = 1;i <= N;++i){
            aa[i] = bb[(id + i - 1 - 1) % N + 1];
        }
        for(int i = 0;i <= N;++i)
        for(int j = 0;j <= B;++j)
        for(int k = 0;k <= 2;++k)
            dp[i][j][k] = -LINF;

        for(int i = 0;i <= N;++i){
            dp[i][0][0] = 0LL;
        }
        for(int i = 1;i <= N;++i){
            for(int j = 1;j <= min(i,B);++j){
                dp[i][j][0] = max(max(dp[i-1][j][0],dp[i-1][j][1]),dp[i-1][j][2]);
                dp[i][j][1] = dp[i-1][j-1][0];
                dp[i][j][2] = max(dp[i-1][j-1][1],dp[i-1][j-1][2]) + aa[i];
            }
        }
        int ans = 0;
        for(int i = B;i <= N;++i){
            for(int j = 0;j < 3;++j){
                ans = max(ans,dp[i][B][j]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}