cf448C

原题链接http://codeforces.com/contest/448/problem/C

题意大概是有n列篱笆,每列宽度都为1,高度为hi,现在有一把刷子宽度为1,每次刷不能改变方向,要么横着刷要么竖着刷,可以刷任意长度,但是方向不能改变,问最少刷多少次能把篱笆刷完。

仔细想一下,发现顶多刷n次就能够刷完,所以最多不会超过n次。而且如果某一列有横着刷的部分,那么下面都是横着刷的,(想一下如果下面是竖着刷,那上面就没有刷的 必要了),所以答案必定要么是某一列一整列都是横着刷的,(反证一下,如果每一列都不是一整列横着刷的,说明n列至少竖着刷了n次再加上横着的就已经>n了,显然不对的);要么当然还有可能是都是竖着刷,答案就是n了。

我们利用答案这个特性,可以想出一个dp状态,记dp[i][j]为前i列全部刷完后,第j列一整列都是横着刷的最小次数,

在h[1]前面加多一个h[0]= 0 ,表示一列都不横着刷的状态,

记minx(j,i)为h[j]到h[i]之间的最小值,

枚举j从0到i-1  如果h[i] <= minx(j , i-1) 那么说明第j列横着刷的可以延伸到第i列 那么dp[i][j] = dp[i-1][j] ;否则 dp[i][j] = dp[i - 1][j] + 1 (既然不能延伸到这里来那么干脆第i列竖着 刷)

dp[i][i] 的状态同样可以由前面转移得到,如果h[i] <= minx(j , i - 1)那么 dp[i][i] = dp[i-1][j]  ;  否则 dp[i][i] = dp[i-1][j] + h[i] - minx ( j , i-1 )(这里表示需要重新再多横着刷几次)【这里注意,j同样是从0到i - 1 ,0也同样需要,代表一行都不横着刷时的状态 ,wa了好久就是因为没有从0开始枚举!!!】

下面是代码

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const int MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
using namespace std;
int minx[6000][6000];
int h[6000] ;
int dp[6000][6000];
int main()
{
    int n ;
    cin>> n;
    h[0] = 0;
    for(int i = 1; i<= n ; ++ i)
        scanf("%d" ,&h[i]);
    for(int i = 0; i <= n ; ++ i)
    {
        minx [i][i] = h[i];
        for(int j = i + 1 ;j <= n ;++ j)
            minx [i][j] = min(minx[i][j-1] , h[j]);
    }
    dp[1][1] = h[1];
    dp[1][0] = 1;
    for(int i = 2 ; i <= n ;++ i)
    {
        for(int j = 0 ; j < i ; ++ j)
        {
            if(h[i] <= minx[j][i-1] )
                dp[i][j] = dp[i-1][j] ;
            else
                dp[i][j] = 1 + dp[i-1][j] ;

        }
        dp[i][i] = 0x3f3f3f3f;
        for(int j = 0; j < i ;++ j)
        {
            if(h[i] <= minx[j][i]  )
                dp[i][i] = min (dp[i][i] , dp[i-1][j]);
            else
                dp[i][i] = min(dp[i][i] , dp[i-1][j] + (h[i] - minx[j][i]));
        }

    }

    int ans = 0x3f3f3f3f;
    for(int i = 0; i<= n ;++ i) ans = min(ans , dp[n][i]);
    cout<<ans<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值