HDU 1421 搬寝室

博客围绕多阶段决策dp展开,还涉及选对物品使每对物品重量平方差和最小的问题。阐述了最优解中每对物品在排序后序列中相邻的结论,通过子问题递推求解,处理时考虑物品选或不选,还提及初始状态设置对结果的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

多阶段决策dp。(另一种思考方式是转化为求 最小的不相邻的要求必须正好选k个的子序列和。。代码一样。。参考这个的第二种方法)
k对物品,要求每对物品重量的平方差的和最小。

这道题必须想清楚一个结论:最优解的每一对物品在排序后的序列中一定都是相邻的。这句话从下面两个方面理解:

  1. 每一对物品相邻 指的是这对物品内的两个物品是相邻的。
  2. 最优解的每一对物品相邻。所以,若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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值