搬寝室 (九度教程第 99 题)
1.题目描述:
搬寝室是很累的,xhd 深有体会.时间追述 2006 年 7 月 9 号,那天 xhd 迫于无奈要从 27 号楼搬到 3 号楼,因为 10 号要封楼了.看着寝室里的 n 件物品,xhd 开始发呆,因为 n 是一个小于 2000 的整数,实在是太多了,于是 xhd 决定随便搬 2k 件过去就行了.但还是会很累,因为 2k 也不小是一个不大于 n 的整数.幸运的是 xhd 根据多年的搬东西的经验发现每搬一次的疲劳度是和左右手的物品的重量差的平方成正比(这里补充一句,xhd 每次搬两件东西,左手一件右手一件).例如 xhd 左手拿重量为 3 的物品,右手拿重量为 6 的物品,则他搬完这次的疲劳度为(6-3)^2 = 9.
现在可怜的 xhd 希望知道搬完这 2k 件物品后的最佳状态是怎样的(也就是最低的疲劳度),请告诉他吧.
输入:
每组输入数据有两行,第一行有两个数 n,k(2<=2k<=n<2000).第二行有 n 个整数分别表示 n 件物品的重量(重量是一个小于 2^15 的正整数).
输出:
对应每组输入数据,输出数据只有一个表示他的最少的疲劳度,每个一行.
样例输入:
2 1
1 3
样例输出:
4
2.基本思路
首先可以知道,对于重量为
a
<
b
<
c
<
d
a<b<c<d
a<b<c<d的四件物品,
(
a
,
b
)
(a,b)
(a,b)和
(
c
,
d
)
(c,d)
(c,d)分别组合会获得的最小的疲劳度。因此对于所给的物品序列
O
b
j
e
c
t
[
n
]
Object[n]
Object[n]我们先对其进行升序排序,然后定义矩阵
d
p
[
k
]
[
n
]
dp[k][n]
dp[k][n],矩阵元素
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前j件物品中选取i对物品所获得的最小疲劳度。对于
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]主要由两种状态转移而来①
o
b
j
e
c
t
[
j
object[j
object[j]和
o
b
j
e
c
t
[
j
−
1
]
object[j-1]
object[j−1]成功组队②
o
b
j
e
c
t
[
j
]
object[j]
object[j]无法和
o
b
j
e
c
t
[
j
−
1
]
object[j-1]
object[j−1]组队,则可以得到如下递推方程:
d
p
[
i
]
[
j
]
=
min
0
≤
i
≤
k
,
2
i
≤
j
≤
n
{
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
−
1
]
[
j
−
2
]
+
(
o
b
j
e
c
t
[
j
]
−
o
b
j
e
c
t
[
j
−
1
]
)
2
}
dp[i][j]=\min\limits_{0≤i≤k,2i≤j≤n}\{dp[i][j-1],dp[i-1][j-2]+(object[j]-object[j-1])^2 \}
dp[i][j]=0≤i≤k,2i≤j≤nmin{dp[i][j−1],dp[i−1][j−2]+(object[j]−object[j−1])2}
需要注意的是
2
i
≤
j
≤
n
2i≤j≤n
2i≤j≤n的限制条件,以及对于每一行开始时
j
=
=
2
i
j==2i
j==2i时dp[i][j]的初始化。
3.代码实现
#include <iostream>
#include <climits>
#include <algorithm>
#define N 2002
using namespace std;
int min(int a,int b){
if(a<b)return a;
else
return b;
}
int object[N];
int dp[N/2][N];//dp[i][j]表示前j件物品中选择i对可以获得的最小疲劳值
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k)){
for(int i=1;i<=n;i++){
scanf("%d",&object[i]);
}
for(int j=1;j<=n;j++){
dp[0][j]=0;
}
sort(object+1,object+1+n);
for(int i=1;i<=k;i++){
for(int j=2*i;j<=n;j++){
if(j==2*i){
dp[i][j]=dp[i-1][j-2]+(object[j]-object[j-1])*(object[j]-object[j-1]);//注意每一行行头的初始化操作
}
else
dp[i][j]=min(dp[i][j-1],dp[i-1][j-2]+(object[j]-object[j-1])*(object[j]-object[j-1]));
}
}
printf("%d\n",dp[k][n]);
}
return 0;
}
/*
2 1
1 3
*/