HDU 1421 搬寝室
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; }