【题目简介】
搬寝室是很累的,xhd深有体会.时间追述2006年7月9号,那天xhd迫于无奈要从27号楼搬到3号楼,因为10号要封楼了.看着寝室里的n件物品,xhd开始发呆,因为n是一个小于2000的整数,实在是太多了,于是xhd决定随便搬2*k件过去就行了.但还是会很累,因为2*k也不小是一个不大于n的整数.幸运的是xhd根据多年的搬东西的经验发现每搬一次的疲劳度是和左右手的物品的重量差的平方成正比(这里补充一句,xhd每次搬两件东西,左手一件右手一件).例如xhd左手拿重量为3的物品,右手拿重量为6的物品,则他搬完这次的疲劳度为(6-3)^2= 9.现在可怜的xhd希望知道搬完这2*k件物品后的最佳状态是怎样的(也就是最低的疲劳度),请告诉他吧.
每组输入数据有两行,第一行有两个数n,k(2<=2*k<=n<2000).第二行有n个整数分别表示n件物品的重量(重量是一个小于2^15的正整数).
对应每组输入数据,输出数据只有一个表示他的最少的疲劳度,每个一行。
【输入样例】
2 1
1 3
【输出样例】
4
【分析&思路】
还是从样例开始,假设我们有a,b,c,d四件东西(a<b<c<d),简易分析可得:ab配对,cd配对效果更好,所以我们决定排序,这里使用函数“sort”进行排序(亦或是函数“qsort”),这里采用贪心的思想,很容易理解。
但是纯贪心+优化也不能AC,于是决定上DP:
用一个w数组储存东西的重量,然后进行排序,数组dp[n][k]来表示在n件物品中搬k对的最佳状态,以下有两种情况【因为不同于之前做过的题,所以详细讨论】:
1.不搬。即在前n - 1件物品中搬k对,那么疲劳值仍为dp[n - 1][k]。
2.搬。那么第n - 1件物品也要同时搬,即在前n- 2件物品中搬k - 1对物品,再搬最后一对物品,那么疲劳值为dp[n - 2][k - 1] + w[n - 1],n - 1是因为对数必然比总物品数少1。
又因为要使疲劳值最小,故转移方程为:
dp[n][k] = min(dp[n - 1][k],dp[n - 2][k - 1] + w[n - 1])
应该注意的是要考虑边界问题,dp[i][j]中:
当2 * j > i时,即要搬的数量超过了物品总量,这是不可能发生的,因此此时令dp[i][j]为无穷大;
当j == 0时,即在一对物品都没搬时,所需疲劳值应该是0,此时令dp[i][j]= 0。
【代码&注释】
#include
#include
//qsort要用
#include
//sort,min要用
using namespace std;
int dp[2001][1001];
int w[2001];
int get(int i, int j)
{
if(j * 2 > i)//要搬的数量超过物品总数时
return 1000000000;//一个足够大的数来表示无穷大,即永远不可能发生
if(j == 0)//一件物品都没搬时
return 0;//不消耗疲劳值
return dp[i][j];//正常情况
}
int main()
{
int n,k,i,j;
while(scanf("%d%d",&n,&k))
{
for(i = 1;i <= n;i++)
scanf("%d",&w[i]);
sort(w + 1, w + n + 1);
//或者qsort(w,n,sizeof(int),cmp),cmp自己写,从大到小,语法和sort中cmp一样
for(i = 1;i < n;i++)
{
w[i] = w[i + 1] - w[i];
w[i] *= w[i];
}
for(j = 1;j <= k;j++)
for(i = 2 * j;i <= n;i++)
dp[i][j] = min(get(i - 1, j), get(i - 2, j - 1) + w[i - 1]);//见转移方程
printf("%d\n",dp[n][k]);
}
return 0;
}