一个很好的dp.

显然最优的方案一定是最瘦的情况,从前往后是没有转移性质的,也不符合dp的最优解转移.

那么考虑从后往前转移,定义:

fif_i表示到了第ii个的{最小宽度是多少,高度是多少},因为到了ii最小宽度的情况下,高度是最高的,顺带记录下高度即可.

转移很显然:

fi.first=sufisufj,fi.second=fj.second+1{f_i.first=suf_i-suf_j,fi.second=f_j.second+1}。当sufi>=sufj+fj.firstsuf_i>=suf_j+f_j.first

由上面的贪心我们知道,jj越小越好.而且fj.first+sufjf_j.first+suf_j一定是要递增的.不然单调性就不成立.

还有一个细节就是并不是你推进来就可以把前面所有比你小的全部删了,因为可能下一个的sufisuf_i不一定不小于当前的sufnow+fnowsuf_{now}+f_{now}.所以我们要保留最后一个元素.

那么我们只要拿一个双端队列记录下转移就好了.

复杂度就可以到达O(N)O(N).

code:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+5;

int w[N],suf[N];
pair<int,int>f[N];

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	for(int i=n;i>=1;i--)
		suf[i]=suf[i+1]+w[i];
	deque<int>q;
	int x=n+1;
	for(int i=n;i>=1;i--)
	{
		f[i]={suf[i],1};
		while(q.size()&&f[q.front()].first+suf[q.front()]<=suf[i])	
		{
			f[i].first=suf[i]-suf[q.front()],f[i].second=f[q.front()].second+1,x=q.front(),q.pop_front();
		}
		q.push_front(x);
		while(q.size()&&f[q.back()].first+suf[q.back()]>f[i].first+suf[i])	q.pop_back();
		q.push_back(i);
	}
	
	printf("%d\n",f[1].second);
	return 0;
}