内鬼们の每日一题day5
题意 :有n块连着的木板,每个木板的高度为 h i h_i hi,你需要把这 n n n块木板上色,每次上色你可以选择竖着刷完一块木板,或者横着刷一个单位高度的连续的木板,问最少需要刷几次。
1 ≤ n ≤ 5000 1 \leq n \leq 5000 1≤n≤5000 , 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1≤ai≤109
样例
输入#1
5
2 2 1 2 1
输出#1
3
输入#2
2
2 2
输出#2
2
有两种刷墙方式:横着、竖着
首先,如果需要横着刷,当前刷的位置的下面也必须横着刷。
考虑一般情况,(假设这里是要先横着刷)每次横着刷完一段连续的木板之后,会将整段木板分成许多不连续的木板段,于是就相当于得到规模更小的问题,
注意考虑如果高度很高,全部竖着涂(贡献 r - l + 1)也许会更优,
分治
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5010;
int n;
ll a[maxn], ans = 0;
ll solve(int l, int r, int h){ //[l, r]区间内,最下面h已经被涂的贡献
// cout << l << " " << r << " " << h << endl;
// system("pause");
if(l > n) return 0;
if(l == r) return 1;
ll mn = 2e9;
for(int i = l; i <= r; ++ i){
mn = min(mn, a[i]);
}
ll tmp = 0; //如果先横着的所有贡献
tmp += mn - h; //先加上连续的横着涂的那段的贡献
for(int i = l; i <= r; ++ i){
if(a[i] == mn) continue; // 就不连续
int j = i;
for(; j < r; ++ j){
if(a[j + 1] == mn) break; // 就不连续
}
tmp += solve(i, j, mn); //转化为更小的规模
i = j + 1;
}
return min(tmp, (ll)r - l + 1); // 是先横着涂更优还是直接全部竖着涂更优
}
int main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
//int n;
cin >> n;
for(int i = 1; i <= n; ++ i){
cin >> a[i];
}
cout << solve(1, n, 0);
return 0;
}