#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 150;
struct book {
int h; int w;
};
book bk[maxn];
int f[maxn][maxn];
int main() {
int n, k;
while (scanf("%d%d", &n, &k) == 2) {
for (int i = 1; i <= n; ++i)scanf("%d%d", &bk[i].h, &bk[i].w);
sort(bk + 1, bk + 1 + n, [](book a, book b)->bool {return a.h < b.h; });/*比较函数要有返回值*/
memset(f, 0, sizeof(f));
int ans = INT_MAX;
for (int i = 2; i <= n; ++i)/*从二开始,因为选一都是零*/
for (int j = 2; j<=min(i,n-k); ++j) {
f[i][j] = INT_MAX;
for (int k = j - 1; k <= i - 1; ++k) {/*左边因为至少已经选取j-1个了,右边因为计算只能算到i-1*/
f[i][j] = min(f[k][j - 1] + abs(bk[k].w-bk[i].w),f[i][j]);/*前一个是先前状态,后一个是由前状态推来的现状态,i是选到的书,j是已选的书*/
if(j==n-k)ans = min(ans, f[i][j]);/*#*/
}
}
printf("%d\n", ans);
}
}
#号注译处我在dp过程中记录答案,但是得不出正解,必需在dp结束后再遍历才能得出正解,奇怪的事。
引用chatgpt部分指引作答:
在代码运行过程中,记录答案有两种方式:
在dp数组中记录最终状态。在dp过程中更新状态时,及时记录下最优状态,最后得到的就是完整解。这种方式需要注意:如果记录最优状态时使用了全局变量,可能会出现多组数据干扰的情况,导致输出结果错误。
在dp过程结束后遍历状态求解。dp结束后遍历dp数组,从中找到符合条件的最终状态,并计算它对应的各项指标,最后才得到完整的答案。这种方式相对安全,但在遍历dp数组时需要考虑边界问题,即不需要枚举所有状态。
您在代码中使用的是第1种方法,在dp过程中记录答案。但是,因为您需要计算的最终状态在dp过程中无法确定,导致您在计算到最终状态时,没有考虑到足够的分支,导致输出的结果与期望值不符。因此,建议您改为第2种方式,在dp过程结束后遍历状态求解。代码如下:
int ans = INT_MAX;
for (int j = 2; j <= n - k; ++j) {
bool flag = true;
for (int i = j; i <= n; ++i) {
if (flag && f[i][j]) {
ans = min(ans, f[i][j]);
flag = false; // 一旦找到满足条件的状态,就不再枚举
}
f[i][j] = min(f[i][j], f[i - 1][j]);
for (int k = j - 1; k <= i - 1; ++k) {
f[i][j] = min(f[i][j], f[k][j - 1] + abs(bk[i].w - bk[k].w));
}
}
}
printf("%d\n", ans);
在这个代码中,我们遍历了所有可能的状态,找到了第$n-k$本书和其他$k$本书组成书堆的最优方案。由于每次枚举正确状态时只执行一遍,所以时间复杂度为$O(n^3)$。