粉刷栅栏 | ||||||
| ||||||
Description | ||||||
给定一组长度为 n 的栅栏,从左到右高度依次是 h[i]。 你需要对这个栅栏粉刷油漆,每次你可以粉刷一行或者一列。 问最少粉刷几次,可以给所有栅栏上漆。(不能多刷) | ||||||
Input | ||||||
第一行包含一个整数,表示栅栏的长度。 接下来的一行,包含 n 个数(n <= 5000),依次表示 h[i](0 <= h[i] <= 10)。 | ||||||
Output | ||||||
输出一行表示对应的答案。 | ||||||
Sample Input | ||||||
5 2 2 1 2 1 | ||||||
Sample Output | ||||||
3 | ||||||
Hint | ||||||
其实本题很好想 做不出来 还是想的太多 就一层一层的刷 刷完这层下层分开了 那就当成新的两个栅栏刷就好了 之后接着再刷 看到这里你可能会想到会用到递归 没错!! 本题就是一个递归的思想解决
如果你每次都分出新的栅栏 就会发现每一层其实只有横着和竖着刷两种刷法 两种刷法的话就很好了 只要计算出横着刷用多少下就好了 当然要和竖着刷的次数比较一下 选择小的记录 竖着刷就是有机列就刷几下么
本题复杂的操作就是判断新的多个栅栏的时候 如何更新
首先把每一个栅栏都有的几层都刷上 然后减去 因为刷完了 就相当于没有这几行 之后在遍历一下 看一看那一段还有栅栏需要刷 在重复之前的操作就好了
用递归 !!
//===================================附代码
#include <iostream>//-递归思想 把每一层分块的部分块的都刷上
#include <cstdio>
#include <algorithm>
#include <cstring>
#define inf 0x3f3f3f3f
using namespace std;
long long s[5030];
long long solve(long long l,long long r)//刷的时候没有竖着的 都是横着刷的
{
long long minn=inf,maxn=-1,tem=0;
for (long long i=l; i<r; i++) //把每行最底下都有的几行给 刷了
if(minn>s[i]) minn=s[i];
for (long long i=l; i<r; i++)
s[i]-=minn; //刷完的剪去
tem+=minn;
for (long long i=l; i<r; i++)
{
if (s[i]>0)
{
for (long long j=i; j<r; j++)//把每行断开的给刷了 并且 记录刷的次数
{
if(s[j]==0||j==r-1&&s[j]!=0) //判断刷的边界
{
if (j==r-1 && s[j]!=0) //此情况下 要处理的话应该使左右大一个
j++;
long long now=solve(i,j); //横着刷上部分的
tem+=min(now,j-i); //比较一下横着刷有多少下 竖着刷有多少下 哪个小 就加上哪个
i=j;
break;
}
}
}
}
return tem;
}
int main()
{
long long n;
while (scanf("%lld",&n)!=EOF)
{
for (long long i=0; i<n; i++)
scanf("%lld",&s[i]);
long long res=solve(0,n);
res=min(res,n); //比较一下横着刷有多少下 竖着刷有多少下 哪个小 就输出哪个
printf("%lld\n",res);
}
}