这道题我认为很好,虽然难想。
题意是给你N个物品的重量,你每次搬两个,搬K次,没次搬的体力消耗是两个物体重量的绝对值差的平方,问怎么搬体力最小,输出最小消耗的体力。
这道题我首先想到的是贪心,但是贪心显然不好贪,我无法证明把物品从小到大排序后每相邻的两个的绝对值差是最小的。
所以我们得用动规的思想,想一想,要想解决搬K次物品体力消耗最小,那我第K-1次是不是也要体力消耗最小,同时,从K-1次转移到K次怎么转移呢?
一下想不出,我也想了好久,还是先去看看数据吧,数据该怎么处理呢,我要搬就会搬两个物品,那么我是不是改把物品两两合成为选择这一对所需要的花费呢?
显然,我们对于排序后的物品重量知道,相邻的一起拿会是最好的,因为如果有a < b < c < d四个数,你会怎么拿?
所以我们用a[i]来表示拿物品i和i+1的代价
这样子我们是不是可以套用背包里面的套路就是
前K次搬运搬哪些物品花费最小
就是dp[前n个物品][k次搬运]
如果我们不拿第n个物品,那么就是dp[前n-1个物品][k次搬运]
如果我们拿第n个物品的话,那么就是dp[前n-2个物品][k-1次搬运]
显然动规方程出来了,但是,问题还没弄完,动态规划的边界问题还需要考虑。
如果我还没搬就是k==0的情况,是不是0
如果k*2>n 就是搬过的次数大于现在的物品量,这显然是不可能的,所有是一个非常大的数
OK
Talk is Cheap , Show me the Code 。
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <stack>
#define inf 1000000000
using namespace std;
int a[2010],dp[2020][2020];
int getnum(int x,int y)
{
if(y==0) return 0;
else if(2*y > x) return inf;
else return dp[x][y];
}
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
for(int i=1; i<n; i++)
{
a[i] = a[i+1] - a[i];
a[i] *= a[i];
}
memset(dp,0,sizeof(dp));
for(int i=1; i<=k; i++)
{
for(int j=i*2; j<=n; j++)
{
dp[j][i] = min(getnum(j-1,i),getnum(j-2,i-1) + a[j-1]);
}
}
printf("%d\n",dp[n][k]);
}
return 0;
}
写完后表示对问题的分析又有一个深的见解。
路还很长,请坚持下去。