题解 P1103 【书本整理】
一道很好的DP入门题,~~适合像我这种学了大半年却还不会DP的人。~~
首先,题目要求我们将书本按照高度排序,在这里我们可以用结构体实现。
其次才是重点,推DP方程。
题目中说抽走K本书,我们不妨转换一下:
从n本书中留下(n - k)本。
然后,寻找每本书之间的关系:
对于第一本书,如果留下,花费显然只能是0。
对于第二本书,如果留下:
1、可以选择将自己作为开头,或者与前面的书连在一起。
2、当然,我们也可以不留下。
对于第三本书,如果留下:
1、自己作为开头
2、与第一本书连接或者与第二本书连接
3、不留下
那么,我们想办法将每本书的与前面的书形成联系:
设置$f_{i,j}$表示第$i$本书,与前面的书连接形成长为$j$的连接。
那么,有:
$f_{i,i}$ = 0
$f_{i,j}$ = min($f_{i,j}$, $f_{i,x-1}$ + $abs$($w_i$ - $w_x$) 其中$x$指与前面连接的书的编号。
那么,废话不多说,上代码:
#include <bits/stdc++.h> using namespace std; #define N 1010 inline int read(){ int x = 0, s = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-')s = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); } return x * s; } int f[N][N]; struct node{ int h, w; } t[N]; bool cmp(node a, node b){ return a.h < b.h; } int main(){ int n = read(), k = read(); for(int i = 1; i <= n; i++){ t[i].h = read(), t[i].w = read(); } sort(t + 1, t + n + 1, cmp); memset(f, 127, sizeof(f)); for(int i = 1;i <= n; i++){ f[i][1] = 0; //只选自己时的费用为0 } for(int i = 2;i <= n; i++){ //试着放第 i 本 for(int j = 1;j <= i - 1; j++){ /*从哪一本开始连接*/ for(int l = 2;l <= min(i, n - k); l++){ /*继承多长*/ f[i][l] = min(f[i][l], f[j][l - 1] + abs(t[i].w - t[j].w)); } } } int ans = (int)9e9; for(int i = 1; i <= n; i++) ans = min(ans, f[i][n - k]); printf("%d\n", ans); return 0; }