当青训营遇上码上掘金
题目
小白要去青训营中进行为期 x 天的训练,青训营使用特制日历,该日历共有 n 月,每一月的包含的天数不一定相同。小白在每一月的第 i 天可以获得 i 个青豆。小白在青训营连续呆 x 天最多可以获得多少青豆
Tips:从最后一个月可以呆到第一个月
输入
- 第一行输入 n : 表示该日历共有 n月。 x : 表示小白在青训营训练的天数
- 第二行输入 n 个数分别表示每个月的天数
提示
- n ≤ 2e5
- 1 ≤ month[i] ≤ 1e6
- 1 ≤ x ≤ sum(month)
示例
输入
3 2
1 3 1
输出
5
解释
将month :{1 3 1} 展开为每一天的收益 => {1 1 2 3 1},连续两天最多可以得到 5 个青豆
解题思路(贪心、前缀和)
- 将month的每一天拉平展开为一个数组,那么该题就可以转换为一个滑窗问题,但是由于x可能很大,所以需要进一步考虑优化
- 要让青豆数量最多,那么只需要窗口末尾在月末就行,可以用反证法证明: 假设窗口末尾不在月末是最优的,设窗口末尾在第k 天是最优的,那么右边一定是第k+1天如果窗口向右滑,由于我们假设不在月末更优,因此窗口的元素和减少,所以从左边出去的元素必然大于k+1,出去的元素的左边那个元素必然大于k,那么把窗口改为向左滑,窗口左边进来一个大于k 的数,右边出去一个等于k的数,窗口元素和变大,矛盾,因此窗口末尾一定在月末是最优的。
- 双指针模拟窗口滑动
代码
import java.io.*;
import java.util.*;
public class Main {
static int M = 1000005;
static long[] sum = new long[M];
public static void main(String[] args) throws IOException {
// 收益的前缀和
for(int i = 1; i < M; i++)
sum[i] = sum[i - 1] + i;
Kattio io = new Kattio();
int n = io.nextInt();
long x = io.nextLong();
int[] month = new int[n];
for(int i = 0 ; i < n; i++)
month[i] = io.nextInt();
long ans = x;
long summ = 0;
// i当前窗口尾, p当前窗口头
for(int i = n - 1, p = n - 1; i >= 0; i--){
while(x > 0){
x -= month[p];
summ += sum[month[p]];
if(p == 0)
p = n - 1;
else
p--;
}
// 去掉大于x天的收益
ans = Math.max(ans, summ - (1-x)*(-x) / 2);
x += month[i];
summ -= sum[month[i]];
}
io.println(ans);
io.close();
}
public static class Kattio extends PrintWriter {
private BufferedReader r;
private StringTokenizer st;
// 标准 IO
public Kattio() {
this(System.in, System.out);
}
public Kattio(InputStream i, OutputStream o) {
super(o);
r = new BufferedReader(new InputStreamReader(i));
}
// 文件 IO
public Kattio(String intput, String output) throws IOException {
super(output);
r = new BufferedReader(new FileReader(intput));
}
// 在没有其他输入时返回 null
public String next() {
try {
while (st == null || !st.hasMoreTokens())
st = new StringTokenizer(r.readLine());
return st.nextToken();
} catch (Exception e) {
}
return null;
}
public int nextInt() {
return Integer.parseInt(next());
}
public double nextDouble() {
return Double.parseDouble(next());
}
public long nextLong() {
return Long.parseLong(next());
}
}
}