题目描述
题目描述
我的某室友学过素描,墙上有n张他的作品。这些作品都是宽度为1,高度不定的矩形,从左到右排成一排,且底边在同一水平线上。
宿舍评比就要来了,为了及格,我们决定买不多于m块的矩形木板,把这些作品和谐掉。要求木板也从左到右排成一排,且底边与作品的底边在同一水平线上。
在能够把所有作品和谐掉的前提下,我们希望这些木板的面积和最小,问最小面积和。
输入格式
第一行两个数n和m,表示作品数和木板数;
第二行n个数Hi,表示从左到右第i个作品的高度。
输出格式
一行一个数ans,表示答案。
样例输入
5 2
4 2 3 5 4
样例输出:
22
数据规模和约定
对于30%的数据:1<=n,m<=10;
对于100%的数据:1<=n,m<=100,1<=Hi<=10000。
分析
-
状态:
d p ( i , j , k ) dp(i, j, k) dp(i,j,k) 表示 i i i 到 j j j 的闭区间内给k个木板能得到最小面积。 -
状态转移方程:
d p ( i , j , k ) = m i n { m a x { a [ i ] . . . . a [ p ] } + d p ( p + 1 , j , k − 1 ) } dp(i, j, k) = min\{max\{a[i]....a[p]\} + dp(p + 1, j, k - 1)\} dp(i,j,k)=min{max{a[i]....a[p]}+dp(p+1,j,k−1)} -
初始状态: (只有木板全部用完才能是最优解)
当 j < i j< i j<i 且 k = 0 k =0 k=0 时, d p ( i , j , k ) = 0 dp(i, j, k)=0 dp(i,j,k)=0, 因为没有剩余剩余素描, 木板也用完。
当 j < i j<i j<i 且 k ≠ 0 k \neq 0 k=0 时, d p ( i , j , k ) = I N F dp(i, j, k)=INF dp(i,j,k)=INF, 因为没有剩余素描, 但是模板没有用完。
当 i ⩽ j i\leqslant j i⩽j 且 k = = 0 k == 0 k==0 时, d p ( i , j , k ) = I N F dp(i, j, k)=INF dp(i,j,k)=INF, 因为有剩余素描, 但是没有木板了。
当 i ⩽ j i\leqslant j i⩽j 且 k ⩾ ( j − i + 1 ) k \geqslant (j - i + 1) k⩾(j−i+1) 时, d p ( i , j , k ) = I N F dp(i, j, k)=INF dp(i,j,k)=INF, 因为有剩余素描, 但板子过多。INF设置为100 * 10000 + 5, 即面积绝对不可能取得的值即可, 使用INT_MAX 存在溢出风险。
记忆化搜索:
#include <bits/stdc++.h>
using namespace std;
const int INF = 1000005;
int dp[101][101][10001];
int a[101], n, m;
// [i, j]
int dfs(int i, int j, int k)
{
// 剪枝
// 还有剩余素描, 但没有板子了
if(j >= i && k == 0) return dp[i][j][k] = INF;
// 还有剩余素描, 但是板子过多
if(j >= i && k > (j - i + 1)) return dp[i][j][k] = INF;
// 素描覆盖完, 但是还有剩余的板子
if(i > j && k != 0) return INF;
// 素描覆盖完, 且没有剩余板子了
if(i > j && k == 0) return 0;
// 在 i, j, 且剩余k块儿木板的最优解
if(dp[i][j][k] != 0) return dp[i][j][k];
int ans = INF;
for(int L = i; L <= j; L++)
{
int now = INT_MIN;
for(int t = i; t <= L; ++t)
{
now = max(now, a[t]);
}
now = now * (L - i + 1);
ans = min(ans, now + dfs(L + 1, j, k - 1));
}
// 记忆化
return dp[i][j][k] = ans;
}
int main()
{
#ifdef LOCAL
freopen("ADV-298.in", "r", stdin);
#endif
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> a[i];
int ans = dfs(1, n, m);
cout << ans << endl;
return 0;
}