题意:有n块连着的木板,每个木板的高度为\(h_i\),你需要把这n块木板上色,每次上色你可以选择竖着刷完一块木板,或者横着刷一个高度单位的连续的木板(不能跳跃),问最少需要刷几次?
分析:先只考虑贪心地横着涂:每一次尽可能地涂最长,且在此次横着涂的下方必定都是横着涂的,因为如果下面有竖着涂的,根据最优性,上一次竖着涂的时候肯定要把此次的也涂掉(这个自己想想很容易明白,对于一根木板,绝对不可能下面竖着涂,上面横着涂)
所以对于一串连着的木板\(h_1\),\(h_2\)...\(h_n\),就必定是从下往上涂min{\(h_1\),\(h_2\)...\(h_n\)}次,这样以后\(h_1\),\(h_2\)...\(h_n\)就被分成了几串不连通的木板(子局面),说到这里,可以发现本题可以用分治解决,时间复杂度\(O(N^2)\).
设work(l,r,now)表示对于[l,r]这一串连着的木板,已经从下往上涂了now格时的答案.
设minh=min{\(h_1\),\(h_2\)...\(h_n\)},则work(l,r,now)=min(r-l+1,\(\sum work(u,v,minh)\)),其中[u,v]是分割出的一个子局面.至于为什么还要与r-l+1取min?因为如果横着涂比直接竖着涂要麻烦,那就不如直接每一根木板都竖着涂呗,一共有r-l+1根木板.
最后注意一下边界情况l=r时,显然只有一根木板的话,那就直接竖着涂一次完事了.
int n,h[5005];
int work(int l,int r,int now){
if(l==r)return 1;//边界情况
int i,j,ans=0,minh=1e9;
for(i=l;i<=r;i++)
if(h[i]<minh)minh=h[i];
//找到[l,r]这一串木板中最短的木板
ans+=minh-now;//记得减去之前已经涂了的now格
for(i=l;i<=r;i++){//分割成子局面并处理
if(h[i]==minh)continue;
for(j=i;j<=r;j++)
if(h[j+1]==minh||j==r)break;
ans+=work(i,j,minh);
i=j+1;//刚开始忘记了这里
}
return min(r-l+1,ans);//与竖着涂取min
}
int main(){
n=read();
for(int i=1;i<=n;i++)h[i]=read();
printf("%d\n",work(1,n,0));
return 0;
}
原文地址:https://www.cnblogs.com/PPXppx/p/10359976.html
时间: 2024-10-16 11:42:38