多阶段决策dp。(另一种思考方式是转化为求 最小的不相邻的要求必须正好选k个的子序列和。。代码一样。。参考这个的第二种方法)
选k对物品,要求每对物品重量的平方差的和最小。
这道题必须想清楚一个结论:最优解的每一对物品在排序后的序列中一定都是相邻的。这句话从下面两个方面理解:
- 每一对物品相邻 指的是这对物品内的两个物品是相邻的。
- 最优解的每一对物品都相邻。所以,若
n=2k,则最优解可以排序后直接得出(因为只有一种构造方式)。
为什么?若某一对物品不相邻,假设中间有其他对物品干扰了,则只有两种情况,而这两种情况都可以转化为更优的方案,即没有重叠计算差值。(下图数轴表示已排序的物品序列)

dp[i][j]表示可选取物品的范围为前i个,选取j对的子问题的最优解。
逐个扩大子问题范围,递推求解。在处理前i个时,前面的子问题都已计算,当前只多了一个可选择的物品i,那么只用考虑选或不选物品i。若选,则只能将物品i和物品i-1配对,由子问题i-2转移。
注意只把初始触发状态设为0(或者加上边界状态(也可以显式排除):所有范围下都选0对)。
虽然是求最小,但如果全设为0也AC,证明了没有引用非法状态。但要是把if(i == 2*j)去掉就WA了(这样的话必须先全设为INF,因为引用了非法状态)。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std; // 可以证明,必须按照从小到大,每一对都是相邻的两个物品,才可能有最小值
int N, K;
int w[2000];
int dp[2000][1000]; // 前i物品选j对的最小值
void init()
{
for (int i = 0; i <= N; i++)
dp[i][0] = 0; // 初始的合法状态
}
int main()
{
for (; ~scanf("%d%d", &N, &K);)
{
init();
for (int i = 1; i <= N; i++)
scanf("%d", &w[i]);
sort(w + 1, w + N + 1);
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= i / 2; j++)
{
if (i == 2 * j)
dp[i][j] = dp[i - 2][j - 1] + (w[i - 1] - w[i])*(w[i - 1] - w[i]);
else
dp[i][j] = min(dp[i - 1][j], dp[i - 2][j - 1] + (w[i - 1] - w[i])*(w[i - 1] - w[i])); // 不选与选
}
}
printf("%d\n", dp[N][K]);
}
return 0;
}
博客围绕多阶段决策dp展开,还涉及选对物品使每对物品重量平方差和最小的问题。阐述了最优解中每对物品在排序后序列中相邻的结论,通过子问题递推求解,处理时考虑物品选或不选,还提及初始状态设置对结果的影响。
274

被折叠的 条评论
为什么被折叠?



