http://acm.hdu.edu.cn/showproblem.php?pid=1421
动态规划题。
我们可以用一个二维dp来存储第有i个物品取拿j次所用的最少体力。首先进行从小到大的排序。则拿相邻的两个最省力。物品重量存在w数组中。
PS:以下数据里面的 i 代表有i个物品, j 代表这 i 个物品分 j 次拿。
因此我们可以增加一个物品 i ,拿取的次数还是不变,则分为两种情况:
1、新增的最后一个不拿,则体力消耗为i-1时拿j次的消耗dp[i-1][j]。 即状态方程为dp[i][j] = dp[i-1][j]
2、最后一个拿,则体力消耗为i-2(i - 1和 i 一起拿着最省力)时的消耗加上i - 1和 i 消耗的体力。 状态方程为dp[i][j] = dp[i-2][j] + (w[i] - w[i-1] * w[i] - w[i-1])
然后取里面小得值即为当前最小值。
综上两种情况,则状态方程为:dp[i][j] = min(dp[i -1][j] , dp[i-2][j] + (w[i] - w[i-1] * w[i] - w[i-1]))
还要注意
1、dp[2][1] = w[2] - w[1] * w[2] - w[1]
2、当取的次数 j*2 == n 的时候,只有一种取法,即dp[i][j] = dp[i-2][j] + (w[i] - w[i-1] * w[i] - w[i-1]) (1,2,3,4取2次,则肯定1和2是一次,2和3是一次)
还是不懂得话直接看代码是最好的。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXX 999999
using namespace std;
int dp[2010][2010];
bool cmp(int a, int b)
{
return a<b;
}
int main()
{
int n,k,i,j,mi;
int w[2010];
while(scanf("%d%d",&n,&k)!=EOF)
{
mi = MAXX;
memset(dp,0,sizeof(dp));
for(i = 1; i <= n; i++)
{
scanf("%d",&w[i]);
}
sort(w+1,w+n+1,cmp);
dp[2][1] = (w[2]-w[1])*(w[2]-w[1]);
// printf("dp[2][1]:%d\n",dp[2][1]);
for(i = 3; i <= n; i++) //i个物品取j次
{
for(j = 1; j <= i/2; j++) //第i个物品分j次拿
{
if(j*2==i) //忘了这个判断了。
{
dp[i][j]=dp[i-2][j-1]+(w[i]-w[i-1])*(w[i]-w[i-1]);
}
else
{
dp[i][j] = min(dp[i-1][j]/*第i个不拿*/,dp[i-2][j-1]+(w[i]-w[i-1])*(w[i]-w[i-1])/*第i个拿*/);
}
}
}
printf("%d\n",dp[n][k]);
}
return 0;
}