题解
这是一道dp题。
状态:(dp[i][j][k])表示前(i)座山上建(j)栋房子,第(i)座山上建/不建((k=1/0))时的最小花费((k=1)时不考虑(a_{i+1}ge a_i)的花费)。
转移方程:
[ dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]+max(0,a[i]-a[i-1]+1));
]
当(a_i)不建房时,需要讨论(a_{i-1})上是否有房子。如果没有直接转移,有的话需加上(a_ige a_{i-1})的高度。因为也有(a_i<a_{i-1})的可能,因此需要(max(0,高度))。
[dp[i][j][1]=min(dp[i-2][j-1][0]+max(0,a[i-1]-a[i]+1),\dp[i-2][j-1][1]+max(0,a[i-1]-min(a[i-2],a[i])+1));
]
当(a_i)建房时,(a_{i-1})上只可没有房子,需要讨论(a_{i-2})上有无房屋。如果没有需加上(a_{i-1}ge a_i)的高度,如果有则(a_{i-1})需同时低于(a_i,a_{i-2}),因此加上(a_{i-1}ge min(a_i,a_{i-2}))的高度。
初始值:因为方程为取(min),初始值需赋极大值,而(dp[i][0][0])也参与转移,需赋值(0)。此外状态(dp[1][1][1])的花费也是(0),需要单独赋值。
答案:(min(dp[n][i][0],dp[n][i][1]),quad 1le ile lfloor frac{n+1}{2} floor)。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int a[N],dp[N][N][2];
int main()
{
int n,st;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<=n;i++) dp[i][0][0]=0;
dp[1][1][1]=0;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=(i+1)/2;j++)
{
dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]+max(0,a[i]-a[i-1]+1));
dp[i][j][1]=min(dp[i-2][j-1][0]+max(0,a[i-1]-a[i]+1),dp[i-2][j-1][1]+max(0,a[i-1]-min(a[i-2],a[i])+1));
}
}
for(int i=1;i<=(n+1)/2;i++) printf("%d ",min(dp[n][i][0],dp[n][i][1]));
return 0;
}