题解 P1103 【书本整理】
先理解题意
对于给出的书本,Frank会先把它们按照高度排好序,接下来通过删去一些书本来达到宽度最整齐;不论怎么删去,都是在原有顺序的基础上抽走。
为我这种DP初学者的详细分析
(以下的“差”指的是差的绝对值)
“抽走”对于整齐度的影响是很奇怪的:减去自己与两旁书本宽度的差,再加上那两书本宽度的差。尽量转化为已学的模型:从
这是不是很熟悉?如果不,我们一本本地尝试加入,那么“当前试着把哪一本加入”就是状态的一个维度(至少要用
取第一本,不用花费(花费即增加“不整齐度”)。
第二本书,
①:假如自顾自,成为一长串书本的队首(也就是忽略第一本,从第二本开始取),不用花费。
②:可是如果接上第一本,要花费,好处是队列长度增加到
到第三本书,
①:如果忽略前两本,不用花费,但是只取了这么一本书。
②:如果从第一本或第二本接上,长度都会变成2,那么我们选择花费小的一种方式。
③:如果前两本都接上,队列成为
尝试加入第四本书,
①:也可以从自己开始取,
②:也可以从
③:也可以从长度为
④:也可以接上
^此时前三本中花费最小、长度为2的队列已经存储在
第五本,第六本依此类推。
#include<bits/stdc++.h>
using namespace std;
int n, k, m, Min = 0x7fffffff;
int f[501][501];
//f[i][l]:以i作末尾,选了l本书时的最小花费
struct info
{
int h, w;
}a[1001];
bool cmp(const info & x, const info & y)
{
return x.h < y.h;
}
int main()
{
cin >> n >> k;
m = n - k;//选取m本书
for(int i = 1; i <= n; i++)
scanf("%d %d", &a[i].h, &a[i].w);
sort(a+1, a+n+1, cmp);//高度决定顺序
memset(f, 20, sizeof(f));//初始极大,能缩小就缩小
for(int i = 1; i <= n; i++)
f[i][1] = 0;
//单独选择任何书都不会有花费
for(int i = 2; i <= n; i++)//试着放第i本的时候
for(int j = 1; j <= i-1; j++)//尝试与前面第j本相邻
for(int l = 2; l <= min(i, m); l++)//放下第i本时,能从之前长1的队列继承为长2的队列,也能从之前长2的队列继承为长3的队列……l表示放下后的长度
//显然试到第i本时,长度不会超过i,也不会超过m,m是最终需要的长度
f[i][l] = min(f[i][l], f[j][l-1] + abs(a[i].w - a[j].w/*这是尝试相邻的书本*/));//放第i本继承到长度为l,总花费越小越好
for(int i = m; i <= n; i++)
Min = min(Min, f[i][m]);//i的循环的意思是:以m结尾的队列,可能最小,以m+1结尾的队列也可能的……以n结尾的队列也可能。
printf("%d\n", Min);
return 0;
}