Codeforces 1987C
codeforces P1987C
给定一个n长度的数组,每一步都要遍历整个数组。如果某个元素是末尾元素或是比其后一个元素大,则该元素减去1直到该元素为0,求解总步数,算法复杂度要求 \(O(n)\)
先给出暴力解法,复杂度 \(O(n^2)\):
int t = 0;
do{
for(int i = 0; i < n - 1; i++){
if(a[i] > a[i + 1]) a[i] = max(0, a[i] - 1);
}
a[n - 1] = max(0, a[n - 1] - 1);
// printf("-----\n");
// for(int i = 0; i < n; i++) printf("%d ", a[i]);
// printf("\n-----\n");
t++;
}while(!jugde(a, n));
return t;
显然会Time limit exceeded,所以要使用不同的算法
考虑到题述中的关键步骤 “如果某个元素是末尾元素或是比其后一个元素大,则该元素减去1直到该元素为0”,可以看到:
1)整个数组中非最大的数字的减少步骤一定包括在最大数字的减少步骤中,如果这个最大数字在第一个。
2)这个最大的数字若不是在第一个,则会存在额外(在这个最大数字之外)的减少步骤。
整个模拟的过程都是在向右比大,但末尾元素却会一直减小,说明了什么。
说明整个模拟过程中,不受条件约束的持续变化的那个元素的位置,应该是数组遍历的起点,因为它代表了一个确定存在的减少步骤
所以应该对整个数组逆向遍历,动态规划方程为:
\[ans=\max(ans+1,h_i)
\]
解释一下:
1)当前的步骤小于该高度 \(h_i\) 时,说明这不是上述的“最大数字的减少步骤”,将其替换
2)当前的步骤大于该高度 \(h_i\) 时,说明该高度的减少步骤被包含在最终的减少步骤里,对应了最后的 1 -> 0,则ans + 1
所以代码为:
int n, t = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
for(int i = n - 1; i >= 0; i--) t = max(t + 1, a[i]);
// printf("%d\n", t == pat(a, n) ? t : -1); //对拍
printf("%d\n", t);
AC