链接: http://codeforces.com/contest/1013/problem/E
题意: 现在有n个山,高度为a ,如果想在一个山上建房子,那么必须要求该山严格高于两边的山,,你可以花费1的时间将一个山的高度降低1 ,问你建1 to (n+1)/2,分别所需要的时间。
思路: 很容易想到,长度为x ,那么我当前的x点建不建 只和他前一个位置状态有关。那么我就可以设 dp[ i ][ j ][ 0/1 ] 分别表示走到位置i建了j 个屋子,最后一个位置建不建, dp方程出来了,但是怎么转移呢,对于如果我当前位置不建房子的话,那么很容易,就直接看他的前一个状态就可以了。但是问题在于我当前位置建房子,那么肯定他的前一个状态是不建房子,那么问题来了,如果我只考虑他的前一个位置状态,那么就有 这样的一个问题 前两个位置状态为 10 并且 前两个的高度为 H i-1>=H i-2,并且如果 Hi <=Hi-1 那么如果只考虑i-1的状态肯定会出错,因为这样就多花费了时间。所以这里我们考虑前边两个位置的状态就可以了。那么如果我这个位置盖房子,那么 前一个状态肯定是 10 或者 00 呗。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf =0x3f3f3f3f;
const int N =5005;
int dp[5005][2505][2];
int a[N];
int n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
a[0]=a[n+1]=-inf;
memset(dp,inf,sizeof(dp));
dp[1][1][1]=0;
dp[1][0][0]=0;
dp[0][0][0]=0;
int lim=(n+1)/2;
for(int i=2;i<=n;i++){
dp[i][0][0]=0;
for(int j=1;j<=lim;j++){
int x,y;
x=max(a[i]+1-a[i-1],0);
dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]+x);
x=max(a[i-1]+1-min(a[i],a[i-2]),0);
y=max(a[i-1]+1-a[i],0);
dp[i][j][1]=min(dp[i-2][j-1][1]+x,dp[i-2][j-1][0]+y);
//cout<<"i "<<i<<" j "<<j<<endl;
//cout<<dp[i][j][0]<<" "<<dp[i][j][1]<<endl;
}
}
for(int i=1;i<=lim;i++){
if(i==lim) printf("%d\n",min(dp[n][i][0],dp[n][i][1]) );
else printf("%d ",min(dp[n][i][0],dp[n][i][1]));
}
return 0;
}