题目概述
有N件物品,要搬走其中2*K件,每件物品都有重量,每次搬两个物品,所得疲劳度为两件物品重量差的平方,求最终疲劳度和的最小值
时限
1000ms/2000ms
输入
第一行两个正整数N,K,下一行N个正整数,代表每件物品的重量,输入到EOF为止
限制
2<=2*K<=N<=2000;1<=物品重量<=2^15
输出
每行一个数,疲劳度和的最小值
样例输入
2 1
1 3
4 1
1 2 3 10
4 2
1 8 9 10
样例输出
4
1
50
讨论
dp,不过有些特殊,因为一个物品只能被搬一次,因而不能简单求出差后排序相加,不过分析可得,若取一物品,要让疲劳最小,则取的另一物品必然是重量最接近的那个,换言之,每次必须取重量相邻的两件,先排序,之后构造一二维矩阵,行号代表当前考虑的物品序号,列号代表最多取这些对物品,有点像背包的二维矩阵了,按照状态转移方程dp[r][c]=min(dp[r-1][c],dp[r-2][c-1]+(nums[r]-nums[r-1])*(nums[r]-nums[r-1])),填写矩阵,不过需要注意两个地方,其一,第一列(列下标0)总是0,因为一个物品也不需要拿,其二,当列下标的二倍大于行下标时,值为INF,因为没有那么多物品可以拿,因而永远拿不完(累死了),按照这些要求,便可得出矩阵,最后取最右下角的值即是结果
等等,为什么这样是正确的?首先既然必须取相邻的两件,不妨设要取便取这一件和重量小于其的第一件,那么对于矩阵中任意位置,若拿,则要拿2件,而由于排序,这两件是相邻的,因而需要向上移动2行,同时,这也算是一对了因而需要左移1列,这就是dp[r-2][c-1],然后加上自身所得疲劳度,若不拿,则当前情况和没考虑这件物品但拿同样数量时是一样的,也就是dp[r-1][c]
另一个问题,如果不拿可以直接抄上一行的话,那一件都不拿岂不是疲劳度0?非也,对第一件物品,由于没法拿,疲劳度都是INF,对第二件,不拿就是抄下来INF,拿的话,是从0加上一个疲劳度,自然拿比较合适,对于矩阵内部的位置,拿不拿确实需要斟酌,但是同样的,由于要拿的数量超过现在考虑的物品数量时值就是INF,因而当要拿的数量刚刚等于考虑的物品数量时,实际上仍然是两个一般大小的数的和和INF比较,这也就保证了每次刚好够拿的时候一定会选择去拿,但当有盈余的时候才去考虑最优,可以通过看调试时矩阵内值的情况来确认这一点
另外,分析矩阵结构,发现每次最多用到上面两行的数据,因而矩阵只要三行足矣,至于如何做到循环利用,只要对行下标模3即可
题解状态
140MS,1736K,891 B,C++
题解代码
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<stack>;
using namespace std;
#define INF 0x3f3f3f3f
#define maxx(a,b) ((a)>(b)?(a):(b))
#define minn(a,b) ((a)<(b)?(a):(b))
#define MAXN 1005
#define memset0(a) memset(a,0,sizeof(a))
int N, K;
int nums[MAXN * 2], dp[3][MAXN];//分别存放原始数据和dp过程处理
int fun()
{
for (int p = 0; p < N; p++) {
scanf("%d", &nums[p]);//input
}
sort(nums, nums + N);//排序以达成相邻最小
for (int p = 0; p < 3; p++) {
for (int i = 1; i <= K; i++)//注意下标从1开始 列0值全为0
dp[p][i] = INF;//初始化矩阵元素为INF 这样后面比较大小才有基准
}
for (int p = 2; p <= N; p++)//第1件物品不用考虑 肯定拿不了 一行都是INF(除了列0)
for (int i = 1; i <= K; i++) {//拿0对物品也不用考虑 肯定是0
dp[p % 3][i] = minn(dp[(p - 1) % 3][i], dp[(p - 2) % 3][i - 1] + (nums[p - 1] - nums[p - 2])*(nums[p - 1] - nums[p - 2]));//这么长其实仍然是按照状态转移方程进行两数比较 不过求平方长一些 以及为节省空间而求模
}
return dp[N % 3][K];
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
while (~scanf("%d%d", &N, &K)) {//input
printf("%d\n", fun());//output
}
}
EOF