C. Painting Fence
题意:
有
n
n
n 块连着的木板,每个木板的高度为
h
i
h_i
hi ,你需要把这
n
n
n 块木板上色,每次上色你可以选择竖着刷完一块木板,或者横着刷一个高度单位的连续的木板,问最少需要刷几次。(注意相当于有一个宽
1
1
1 的刷子,每次可以刷无限长度但是高度只有
1
1
1 或者刷无限高度但宽度只有
1
1
1)
思路:
题解
贪心+分治递归
对于区间
[
l
,
r
]
[l,r]
[l,r],首先考虑横着刷,也就是需要取
M
i
n
=
m
i
n
(
a
l
,
a
l
+
1
,
.
.
.
,
a
r
)
Min = min(a_l,a_{l+1},...,a_r)
Min=min(al,al+1,...,ar),先横着刷
M
i
n
Min
Min 次,然后因为
M
i
n
Min
Min 会把
[
l
,
r
]
[l,r]
[l,r] 分割乘若干区间,继续递归求解,典型的分治思想,注意还要比较当前区间横着刷更优还是竖着刷更优
S
T
ST
ST表 预处理一下区间最小值,当然本题
n
2
n^2
n2 也可以过
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 5e3 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll a[maxn];
int Log[maxn], st[maxn][25];
int find(int l, int r)
{
int s = Log[r - l + 1];
return min(st[l][s], st[r - (1 << s) + 1][s]);
}
int dfs(int l, int r, int h)// h表示h以下已经全部涂完了
{
if(l < 1 || r < 1 || l > r) return 0;
if(l == r) return 1ll;
int Min = find(l, r);// 取当前区间最小值,最小值会把 [l,r] 若干区间,就继续分治
int last = l, ans = Min - h;//横着涂
for(int i = l; i <= r; ++i) if(a[i] == Min)
ans += dfs(last, i - 1, Min), last = i + 1;
ans += dfs(last, r, Min);
return min(ans, r - l + 1);//比较横着涂更优还是竖着涂更优
}
void work()
{
for(int i = 2; i <= maxn - 9; ++i) Log[i] = Log[i >> 1] + 1;
cin >> n;
for(int i = 1; i <= n; ++i) cin >> a[i], st[i][0] = a[i];
for(int j = 1; j <= 21; ++j)
for(int i = 1; i + (1 << j) - 1 <= n; ++i)
st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
cout << dfs(1, n, 0) << endl;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}