纪念一下自己上绿了。

下面是题目:

题意:当一个数满足大于他相邻的数的时候,可以在他这建房子,我们每次操作可以将某个数减1,问最少操作多少次可以满足(1~(n+1)/2)。
思路:
一开始想的是定义状态是f[i][j]表示考虑前i个建立j个房子的最小操作数,后来发现好像不够,因为我们很难判断出第i个第i-1第i-2个数之间的关系,那么我们还是加一维吧,表示第i个数是否建立房子,并且只考虑前i个,不考虑i+1个,那么初始化的时候把所有建房子的数量为0的方案数的步数变为0,f[1][1][1]也变成0,状态转移方程:
第i个数不建立房子的方程很好说,
f[i][j][0]=min(f[i-1][j][1]+max(0,a[i]-a[i-1]+1),f[i-1][j][0]);只需要看他前一个房子是否建立,不建立就啥也不动,建立的话就要减少到前一个房子-1
第i个数如果建立房子就要分类讨论了。第i个建立那么第i-1的地方一定不能建立,所以肯定是由f[i-1][j-1][0]转移过来的,接下来我们要找到f[i-1][j-1][0]的前驱,前驱如果是i-2 1的话,也就是i-2的位置建立房子,那么我们减去i-1
之前减去的长度,然后将i-1减到满足比i-2小并且比i小的最大高度。反之,如果i-2没有建立房子,那么我们只需要满足i-1的位置比i低就行了。
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=5010;
int n;
int a[N];
int f[N][N/2][2];	//只考虑前i个并且第i个(建或者不建)已经建了j个房子的最小操作数
int main()
{
   
	memset(f,0x3f,sizeof f);
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=0;i<=n;i++)
		f[i][0][0]=0;
		f[1][1][1]=0;
	for(int i=2;i<=n;i++)
	{
   
		for(int j=1;j<=(1+i)/2;j++)
			{
   
				f[i][j][0]=min(f[i-1][j][1]+max(0,a[i]-a[i-1]+1),f[i-1][j][0]);
				if(f[i-1][j-1][0]==f[i-2][j-1][1]+max(0,a[i-1]-a[i-2]+1))
				{
   
					f[i][j][1]=min(f[i][j][1],f[i-1][j-1][0]-max(0,a[i-1]-a[i-2]+1)+max(0,max(a[i-1]-a[i-2]+1,a[i-1]-a[i]+1)));
				}
				else
					f[i][j][1]=min(f[i-1][j-1][0]+max(0,a[i-1]-a[i]+1),f[i][j][1]);
			}

	}
	for(int i=1;i<=(1+n)/2;i++)
		cout<<min(f[n][i][0],f[n][i][1])<<' ';
		cout<<endl;
	return 0;
}