洛谷题面

题目大意

\(N\) 本书,第 \(i\) 本书宽 \(w_i\),高 \(h_i\)

我们需要将这些这些书分成几个区间,每个区间所有书的宽度之和不超过 \(L\)

书架的高度为每个区间最高的书的高度之和,求书架最小高度。

题目分析

我们令 \(dp[i]\) 表示到第 \(i\) 本书为止,书架的最小高度。

对于每一个状态 \(dp[i]\),都可能由两种方法转移而来:

  1. 再开一个书架。此时有

\[dp[i]=dp[i-1]+h[i] \]

\(h[i]\) 表示每一本书的高度。

  1. 插入之前的书架,那么当然需要知道前面数的最大高度,\(O(N)\) 查找即可。有

\[dp[i] = \min\{dp[i],dp[j-1]+\operatorname{maxsum}\} \]

\(\operatorname{maxsum}\) 表示最大高度。

代码

//2021/9/6

//2021/9/8

#include <iostream>

#include <cstdio>

#include <algorithm>

#define debug(c) cerr<<#c<<" = "<<c<<endl

namespace Newstd
{
	inline int read()
	{
		char c;
		bool flag=false;
		while((c=getchar())<'0' || c>'9')
		{
		    if(c=='-') flag=true;
		}
		int res=c-'0';
		while((c=getchar())>='0' && c<='9')
		{
		    res=(res<<3)+(res<<1)+c-'0';
		}
		return flag?-res:res;
	}
	inline void print(int x)
	{
		if(x<0)
		{
			putchar('-');x=-x;
		}
		if(x>9)
		{
			print(x/10);
		}
		putchar(x%10+'0');
	}
}

using namespace Newstd;

using namespace std;

const int ma=100005;

int h[ma],w[ma],sum[ma],dp[ma];

int n,m;

int main(void)
{
	n=read(),m=read();
	
	for(register int i=1;i<=n;i++)
	{
		h[i]=read(),w[i]=read();
		
		sum[i]=sum[i-1]+w[i];
		
		dp[i]=(1<<29);	
	}
	
	for(register int i=1;i<=n;i++)
	{
		dp[i]=dp[i-1]+h[i];
		
		int tmp=h[i];
		
		for(register int j=i-1;j>=1 && sum[i]-sum[j-1]<=m;j--)
		{
			tmp=max(tmp,h[j]);
			
			dp[i]=min(dp[i],dp[j-1]+tmp);
		}
	}
	
	print(dp[n]);
	
	return 0;
}