3. B. Milena and Admirer(贪心、小结论、思维)

题目链接

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]xx插入原位置,要求 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 cnt1次,还需要去维护一个最小值 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_back

using 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;
}

总结

这道题目主要是要想清楚对于一个数如何拆分才能对后面的影响最小。


  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值