NC227595 跳跳跳

题目链接

题目

题目描述

dd在玩跳格子游戏,具体游戏规则如下,

\(n\) 个格子呈环形分布,顺时针方向分别标号为 \(1\sim n\) ,其中 \(1\)\(n\) 相邻,每个格子上都有一个正整数 \(a[i]\) ,玩家可以选择一个点作为起点开始跳 \(n\) 下,第 \(i\) 次跳跃,玩家只可以选择当前位置左边或右边最近且尚未被跳跃过的位置进行一次跳跃,并获得 \(i\times a[p]\) 的得分,其中 \(p\) 为第 \(i\) 次跳跃的位置。

dd很鸡贼,想赢又不想动脑子,她希望你能给她规划路线以确保她的胜利

输入描述

第一行一个数 \(n(1≤n≤2000)\)
接下来一行 \(n\) 个数,表示 \(a[i](1≤a[i]≤2000)\)

输出描述

一个数,表示dd可能获得的最高分

示例1

输入

3
1 1 1

输出

6

说明

可能方案
1->2->3
1->3->2
2->1->3
2->3->1
3->1->2
3->2->1
(以上数字表示格子标号)

答案均为 1*1+2*1+3*1=6
最优方案不唯一,最优答案唯一

示例2

输入

3
1 2 3

输出

14

说明

方案:1->2->3(数字对应格子标号)
答案:1*1+2*2+3*3=14
最优方案唯一

题解

知识点:区间dp。

题意就是每次选区已扩展区间的左右侧最近的一个点,起点随意,因此是个环状区间dp可以解决的问题。

开两倍端点,处理环状。转移方程为:

\[dp[i][j] = \max(dp[i + 1][j] + l * a[i], dp[i][j - 1] + l * a[j]) \]

表示为选择 \([i,j]\) 的左端点或右端点作为第 \(l\) 个选择的对象。

类似问题有POJ2287的田忌赛马,也是选择区间左右端点的区间dp,但区别在于这道题是起点确定,田忌赛马是终点确定,选择的是第 \(n-l+1\) 个。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int a[2007 << 1];
ll dp[2007 << 1][2007 << 1];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        a[i + n] = a[i];
    }
    for (int i = 1;i <= 2 * n;i++) dp[i][i] = a[i];
    for (int l = 2;l <= n;l++) {
        for (int i = 1, j = l;j <= 2 * n;i++, j++) {
            dp[i][j] = max(dp[i + 1][j] + l * a[i], dp[i][j - 1] + l * a[j]);
        }
    }
    ll ans = 0;
    for (int i = 1;i <= n;i++) ans = max(ans, dp[i][i + n - 1]);
    cout << ans << '\n';
    return 0;
}
posted @ 2022-08-15 22:24  空白菌  阅读(105)  评论(0编辑  收藏  举报