问题描述:
一觉醒来,你穿越到异世界,成了一名落魄的剑客;此时你身无分文,仅有一把破旧的铁剑以及看穿工匠能力的黄金瞳。为了解决吃住问题,你需要狩猎城外的野怪以换取工会报酬,然而自己的铁剑品级低到砍不破野怪的皮肤,同时自己又没有钱请铁匠锻造升级铁剑。
正在你一筹莫展之际,铁匠联盟举办义务锻造活动,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个金币。
提示
- 需认真分析你最多有三次使用权力的机会,并且至少要通过三个不同的铁匠这两个条件,将问题转化为常见的算法模型进行处理,比如说子数组和的模型处理,你委托过的连续的铁匠都可以看成整个数组的子数组。
- 难点是环形数组,如何转化为非环形数组解决。
- 本题时间复杂度O(n),在输入输出的时候尽量使用scanf和printf而不是cin,cout。
- 你在整个过程中,铁剑的价值可能小于将要贬值的数目,之后铁剑的价值为负数。
思路:
#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;
}