落魄的剑客【DP】

问题描述:

一觉醒来,你穿越到异世界,成了一名落魄的剑客;此时你身无分文,仅有一把破旧的铁剑以及看穿工匠能力的黄金瞳。为了解决吃住问题,你需要狩猎城外的野怪以换取工会报酬,然而自己的铁剑品级低到砍不破野怪的皮肤,同时自己又没有钱请铁匠锻造升级铁剑。
正在你一筹莫展之际,铁匠联盟举办义务锻造活动,n位铁匠围成环形,参与活动的剑客可带着破旧的铁剑(价值为0金币)顺时针请铁匠帮忙锻造。铁匠一共有三种,手艺精湛的铁匠通过锻造能让你的铁剑增值;水准较差的铁匠会让你的铁剑贬值;能力平平的铁匠锻造后,铁剑价值不增不减。主办方规定,剑客必须从任意位铁匠处出发,让一圈铁匠打造后回到原地,并且至少有三位不同的铁匠锻造过你的剑。与此同时,出于对剑客权益的保护,每个剑客享有 “跳过任意多个连续铁匠” 的权利,但这样的权利使用次数有限,至多使用三次。
你清楚,铁剑价值越高,就意味着更高狩猎效率,酬金会如流水般汇入你的账户。就这样,拥有看穿铁匠水平的黄金瞳的你,摸了摸手中价值为0金币的铁剑,心中略一盘算,便带着微笑报名活动了,请问经过活动之后,你的剑最多能值多少金币?

输入格式

第一行为整数n,表示活动中围成环的铁匠个数,铁匠不会中途退场,n<=1000000
第二行到第n+1行,每一行为整数m,是你的黄金瞳看到的不同铁匠的能力值。若m为正整数,则表示经过该铁匠后,你的铁剑会升值m个金币;m为0表示该铁匠后,铁剑价值不会变动;m为负数表示该铁匠水平不高,他的锻造会让你的铁剑价值降低对应个数的金币,-1000<=m<=1000。
从第二行到第m+1行,是按照顺时针给出的铁匠能力值,同时剑客参与锻造的时候也只能顺时针走。
如果铁剑现有价值少于锻造贬值数目,则锻造后,铁剑的价值为负数(此时剑客的铁剑带有诅咒,可能会对主人造成一定伤害)。此外,由于铁匠们围成了环形,其中第二行和第m+1行铁匠是相连的。

输出格式

输出一个数,表示你从圈中任意一位铁匠出发,沿着固定方向请铁匠们锻造,转一圈回到原地,并且至少委托三个不同铁匠所能获得的最高的铁剑价值,其中在每个铁匠处只能锻造一次,你至少使用三次权力,跳过任意多个铁匠。
样例1:

10
1
2
-4
5
6
-9
10
-22
-100
1

样例2:

10
-1
-2
-3
-4
100
-5
-6
-7
-8
-9

输出样例

输出样例1:
25
解释:你从第十位铁匠处(能升值1金币的那位)出发,至少经过三个不同铁匠,路线是 第十位铁匠(升值1金币)-> 第一位铁匠(升值1金币)-> 第二位铁匠(升值2金币),jump,第四位铁匠(升值5金币)-> 第五位铁匠(升值6金币),jump,第七位铁匠(升值10金币),jump回到第十位铁匠处,经过超过三个铁匠锻造,jump次数不大于三,手中铁剑价值25个金币。
输出样例2:
97
解释:你从第一位铁匠处(能贬值1金币)出发,至少通过三个不同驿站,经过路线为:第一位铁匠(贬值1金币)-> 第二位铁匠(贬值2金币),jump,第五位铁匠(升值100金币),jump,回到第一位铁匠处。经过超过三位铁匠锻造,jump次数不大于三,手中铁剑价值97个金币。

提示

  1. 需认真分析你最多有三次使用权力的机会,并且至少要通过三个不同的铁匠这两个条件,将问题转化为常见的算法模型进行处理,比如说子数组和的模型处理,你委托过的连续的铁匠都可以看成整个数组的子数组。
  2. 难点是环形数组,如何转化为非环形数组解决。
  3. 本题时间复杂度O(n),在输入输出的时候尽量使用scanf和printf而不是cin,cout。
  4. 你在整个过程中,铁剑的价值可能小于将要贬值的数目,之后铁剑的价值为负数。

思路:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;

int x[N];
int dp[N][5]; // 到di个人,取了j块 且 第i个人一定要取 的最大价值
int mmax[5]; // 取i块能到达的最大值

int main()
{
    int n,ans;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&x[i]);
    memset(dp,-0x3f,sizeof(dp));
    memset(mmax,-0x3f,sizeof(mmax));
    mmax[0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=min(i-1,3);j++) // 能跟的情况下
            dp[i][j] = max(dp[i-1][j] + x[i],dp[i][j]); // 跟一个,不浪费次数
        for(int j=1;j<=min(i,3);j++)
            dp[i][j] = max(dp[i][j],mmax[j-1] +x[i]); // 使用一次次数
        for(int j=1;j<=min(i,3);j++)
            mmax[j] = max(mmax[j],dp[i][j]);
    }
    ans = mmax[3];
    //接下来处理环形这个问题
    //考虑第一个点取,最后一个点也取且取了四块的情况,只有这种情况能使用到环形
    //更换初始值和上限即可
    memset(dp,-0x3f,sizeof(dp));
    memset(mmax,-0x3f,sizeof(mmax));
    mmax[0]=0;
    dp[1][1] = x[1];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=min(i-1,4);j++)
            dp[i][j] = max(dp[i-1][j] + x[i],dp[i][j]);
        for(int j=2;j<=min(i,4);j++) // 唯一的不同在于不能重新起一行
            dp[i][j] = max(dp[i][j],mmax[j-1] +x[i]);
        for(int j=1;j<=min(i,4);j++)
            mmax[j] = max(mmax[j],dp[i][j]);
    }
    ans = max(ans,dp[n][4]);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值