题目:
题目描述:
给定一个长度为 n 的数组 a
现在有一个长度为初始全为 0 的数组 b
每次操作可以选择一个下标 i ,进行 或
问最少多少次操作可以让 b 数组严格升序?
思路:
无论怎样,这个最终的b数组必定有0,原因如下:
如果最终的 b 数组构成了严格升序的状态但是没有 0 ,那么将其中最接近 0 的那个数变成 0 可以节省至少一步操作,且不改变 b 数组严格升序的状态。
所以得出结论,b数组中必有一个0
那么我们可以从这个0入手,依次遍历0在不同位置时候的操作次数,并找到其实中的最小值。
对于遍历的每一次,我们怎么计算操作次数?
我们还是从 0 入手,0 的位置定下来了之后,依次向两边延申进行推导
我们只需要让外部的位置( )不断操作(加上或者减去 )使得他满足递增的序列,并记录这些操作次数即可。
上面是正常的推导状态,但是因为题目要求只是求出最小的操作次数,所以我们可以把目标转换成由 0 向左向右都递增(下图右侧),在后续的计算中更加方便计算。(至少作者这么认为)
在这一次循环中,如果用 while 对每一位求操作数就太慢了,我们可以直接用公式来求得操作次数:
当前位置需要的操作次数 = 上一位置b数组的值 / 当前a数组的值 + 1
思路有了,具体操作请看AC代码
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
const ll inf = 4e18 + 5;
ll a[N];
ll b[N];
int main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll n;
cin >> n;
ll cnt = inf;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int j = 1; j <= n; j++)
{
memset(b, 0, sizeof(b));
ll cntt = 0;
for (int i = j - 1; i >= 1; i--)
{
ll temp;
temp = b[i + 1] / a[i] + 1;
cntt += temp;
b[i] = a[i] * temp;
}
for (int i = j + 1; i <= n; i++)
{
ll temp;
temp = b[i - 1] / a[i] + 1;
cntt += temp;
b[i] = a[i] * temp;
}
cnt = min(cnt, cntt);
}
cout << cnt << '\n';
return 0;
}