题目链接
B. Milena and Admirer
题意
给一个长度为 n n n的序列,我们通过操作使这个序列变成非递减序列
操作:对 a [ i ] a[i] a[i],我们将 a [ i ] a[i] a[i]删除,将 a [ i ] − x 、 x a[i]-x、x a[i]−x、x插入原位置,要求 x < a [ i ] x<a[i] x<a[i]
求最小的操作次数
题解
考虑贪心
对于满足 a [ i ] < = a [ i + 1 ] a[i]<=a[i+1] a[i]<=a[i+1]的 i i i我们不去操作
对于不满足条件的 i i i,我们考虑将 a [ i ] a[i] a[i]拆成k个满足条件并且尽可能大的数。(这里k最多为 a [ i ] − 1 a[i]-1 a[i]−1因为一个数最多全部拆成1)。
考虑如何拆才能使答案最优,我们希望当前拆完的数尽可能大,在满足拆完的所有数都小于 a [ i + 1 ] a[i+1] a[i+1]的情况下。,因为我们当前拆完的数的最小值会影响前面的数的拆分,我们肯定是希望拆完之后最小值最大的,如何才能达到最小值最大呢?
考虑去平均拆。
对于 a [ i + 1 ] > a [ i ] a[i+1]>a[i] a[i+1]>a[i]需要将拆 c n t = a [ i ] / a [ i + 1 ] + ( a [ i ] cnt=a[i]/a[i+1]+(a[i]%a[i+1]!=0) cnt=a[i]/a[i+1]+(a[i]成这么多个数,需要拆
c n t − 1 cnt-1 cnt−1次,还需要去维护一个最小值 a [ i ] / c n t a[i]/cnt a[i]/cnt,从后往前扫一遍维护最小值,统计答案即可。
代码
#include <bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_backusing namespace std;void solve() {int n;cin>>n;vector<int>a(n+1);rep(i,1,n) {cin>>a[i];}int cur=1e9;int ans=0;//倒着枚举一遍fep(i,n,1) {if(a[i]<cur){cur=a[i];}else if(a[i]%cur==0){ans+=a[i]/cur-1;}else{int cnt=a[i]/cur+1;ans+=(cnt-1);cur=a[i]/cnt;}}cout<<ans<<endl;
}signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// freopen("1.in", "r", stdin);int _;cin>>_;while(_--)solve();return 0;
}
总结
这道题目主要是要想清楚对于一个数如何拆分才能对后面的影响最小。