HDU1421:搬寝室(线性dp)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1421

又是一道,没有思想的题,看了题解,我发现我的dp题几乎都看了题解,我总是想不好状态转移方程,汗颜,以后怎么比赛啊。

先排序,然后说一个数学问题。

首先,要怎么搬呢?即每一对要怎么取?如果有abcd四个数,且a<b<c<d,应该是取ab,cd好呢还是ac,bd好?抑或是bc,ad好呢?答案是第一种,因为:

(a-b)^2+(c-d)^2 < (a-c)^2+(b-d)^2
(a-b)^2+(c-d)^2 < (a-d)^2+(b-c)^2

即每对物品都应是重量最为接近的物品,也就是说对n件物品排序后,每对物品都应该是连续的

定义数组w[i]为搬第i对物品所消耗的疲劳值;数组dp[n][k]来表示在n件物品中搬k对的最佳状态,而达到这一状态的决策可能为:

  1. 第n件物品不搬,即在前n - 1件物品中搬k对,那么疲劳值仍为dp[n - 1][k];
  2. 第n件物品要搬,那么根据上面所证,第n - 1件物品也要同时搬,即在前n - 2件物品中搬k - 1对物品,再搬最后一对物品,那么疲劳值为dp[n - 2][k - 1] + w[n - 1]。

为使疲劳值最小,因此最佳策略为取两种决策中的最小值,即应使:

dp[n][k] = min(dp[n - 1][k], dp[n - 2][k - 1] + w[n - 1])

 

应该注意的是要考虑边界问题,dp[i][j]中:

  1. 当2 * j > i时,即要搬的数量超过了物品总量,这是不可能发生的,因此此时令dp[i][j]为无穷大;
  2. 当j == 0时,即在一对物品都没搬时,所需疲劳值应该是0,此时令dp[i][j] = 0。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <math.h>
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
int n,k,w[2010],dp[2010][1010];

int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
        }
        sort(w+1,w+1+n);
        for(int i=1;i<n;i++)
        {
            w[i]=w[i+1]-w[i];
            w[i]=w[i]*w[i];
        }
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=k;j++)
            {
                dp[i][j]=inf;
            }
        }
        for(int i=0;i<=n;i++)
        dp[i][0]=0;      //边界的处理
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j*2<=i;j++)
            {
                dp[i][j]=min(dp[i-2][j-1]+w[i-1],dp[i-1][j]);
            }
        }
        printf("%d\n",dp[n][k]);
    }
    return 0;
}

 

posted @ 2015-03-27 21:26  人艰不拆_zmc  阅读(220)  评论(0编辑  收藏  举报