书本整理
题目描述
Frank
是一个非常喜爱整洁的人。他有一大堆书和一个书架,想要把书放在书架上。书架可以放下所有的书,所以Frank
首先将书按高度顺序排列在书架上。但是Frank
发现,由于很多书的宽度不同,所以书看起来还是非常不整齐。于是他决定从中拿掉k本书,使得书架可以看起来整齐一点。
书架的不整齐度是这样定义的:每两本书宽度的差的绝对值的和。例如有4本书:
1
×
2
1 \times 2
1×2
5
×
3
5 \times 3
5×3
2
×
4
2 \times 4
2×4
3
×
1
3 \times 1
3×1
那么Frank
将其排列整齐后是:
1
×
2
1 \times 2
1×2
2
×
4
2 \times 4
2×4
3
×
1
3 \times 1
3×1
5
×
3
5 \times 3
5×3
不整齐度就是
2
+
3
+
2
=
7
2+3+2=7
2+3+2=7
已知每本书的高度都不一样,请你求出去掉k本书后的最小的不整齐度。
题解
用 dp[i][j] 表示:前 i 本书,选中 j 本书,并且第 i 本被选中,有最小不整齐度。
所以有状态转移方程:
f i , j = m i n ( f i , j , f p , j − 1 + ∣ w p − w i ∣ ) f_{i, j} = min(f_{i, j}, f_{p, j - 1} + \lvert w_{p} - w_{i} \rvert) fi,j=min(fi,j,fp,j−1+∣wp−wi∣),其中 j − 1 ⩽ p < i j-1\leqslant p < i j−1⩽p<i。
代码(cpp):
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
struct book
{
int h, w;
} books[101];
int cmp(book a, book b)
{
return a.h > b.h;
}
int main()
{
int n, k; // n 本书,去掉 k 本
int m; // 还剩 m 本
int i, j, p;
int dp[101][101]; // dp[i][j],前 i 本选中 j 本
int ans;
scanf("%d %d", &n, &k);
for (i = 0; i < n; ++ i)
scanf("%d %d", &books[i].h, &books[i].w);
sort(books, books + n, cmp);
memset(dp, 0x3f, sizeof(dp)); // 每字节填充 0x3f
for (i = 1; i < n + 1; ++ i)
dp[i][1] = 0;
m = n - k;
for (i = 2; i < n + 1; ++ i) // 前 i 本
for (j = 2; j < m + 1 && j < i + 1; ++ j) // 选中 j 本
for (p = j - 1; p < i; ++ p) // 前 p 本选 j - 1 本
dp[i][j] = min(dp[i][j], dp[p][j - 1] + abs(books[p - 1].w - books[i - 1].w));
ans = dp[m][m];
for (i = m + 1; i < n + 1; ++ i)
ans = min(ans, dp[i][m]);
printf("%d", ans);
return 0;
}