CodeForces-1491C Pekora and Trampoline 贪心,递推
题意
一条直线上有(n)个点,每次可以选择一个起点,若该点为(S_i),则会跳到(i + S_i),(不超过N)且(S_i --)(不小于1)。
问最少的放置次数,让所有(S_i = 1)。
[1 leq n leq 5000\
1leq S_i leq 10^9
]
分析
想到了贪心,但没想到怎么实现。
贪心:每次一定从最开始的不是1的点开始放。
这样做也就是每次把当前点从(S_i)变为(1)的过程,考虑两个方面:之前的点对当前点的影响;当前点对之后的点的影响。
令(t_i)表示之前点对当前点的影响
显然若(t_i >= S_i - 1),那么当前点已经是(1)了,那么当前点对之后点的影响:只会影响(i+1),影响的大小是(t_i - S_i + 1)。
若(t_i < S_i - 1),那么当前点还要重置(S_i - 1 - t_i)次,当前点对之后点的影响:不影响(i + 1),影响([i+2,min(n,i+S_i+1)])各一次。
这样,复杂度O(n^2)
代码
#include<bits/stdc++.h>
#define pii pair<ll,int>
#define eps 1e-7
#define equals(a,b) (fabs(a - b) < eps)
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
const ll MOD = 1e9 + 7;
ll rd(){
ll x = 0;
int f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void solve(){
int n = rd();
vector<int> v(n + 1);
vector<ll> cur(n + 1,0);
for(int i = 0;i < n;i++)
v[i] = rd();
ll ans = 0;
for(int i = 0;i < n;i++){
ll tmp = cur[i];
if(tmp < v[i] - 1) {
ans += v[i] - 1 - tmp;
tmp = v[i] - 1;
}
cur[i + 1] += tmp - v[i] + 1;
for(int j = i + 2;j < min(n,i + v[i] + 1);j++)
cur[j]++;
}
cout << ans << '
';
}
int main(){
int T = rd();
while(T--)
solve();
}