JOBDU-OJ 1452 搬寝室

来源:互联网 发布:英国本科留学条件知乎 编辑:程序博客网 时间:2024/05/22 03:43

题目描述:
搬寝室是很累的,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


分析:
这是一道动态规划题,可以证明,每次拿一对重量相邻物品产生疲劳度才是最小的。于是首先对将物品按重量排序。
如果正确地得出每一次决策的状态至关重要。在本题目中,对于每个物品j每次决策只有两种:

  1. 把物品j和j-1的组成一对,拿走,此时的疲劳度应当为之前的疲劳度加上本次产生的疲劳度
  2. 不拿j,此时的疲劳度同之前不变

用dp[i][j]表示前j个物品组成i对产生的最小疲劳度,状态转移方程如下

dp[i][j] = min( dp[i][j-1], dp[i-1][j-2] + tiredValue(j) )                其中 tiredValue(j)=(list[i] - list[i-1])^2注意当j=2*i时,必须要拿物品j和j-1,这样才能组成i对:dp[i][j] = dp[i-1][j-2] + tiredValue(j)        ( j=2*i )

源码如下:

#include<stdio.h>#include<algorithm>using namespace std;#define INF 0x7fffffffint n,k;int dp[1001][2002];int list[2002];int tiredValue(int i){    return (list[i] - list[i-1]) * (list[i] - list[i-1]);}int min(int a,int b){    return a<b ? a: b;}int main(){    while( scanf("%d%d", &n,&k) != EOF){        for(int i = 1; i<=n; i++){            scanf("%d",&list[i]);        }        sort(list + 1, list + n + 1);        for(int j=0; j<=n; j++)          //前j个物品中拿0对的疲劳度为0            dp[0][j] = 0;        for(int i=1; i<=k; i++)              //对于使用前j个物品拿i对的最小疲劳度            for(int j=i*2; j<=n; j++){                //有两种情况:不拿物品j,那么此时状态就是dp[i][j-1]                //拿物品j和j-1,那么此时状态是dp[i-1][j-2] + tiredValue(i)                //有一种情况是必须拿的,那就是当j=i*2时,必须要拿j和j-1才能组成i对                if(j == i*2){                    dp[i][j] = dp[i-1][j-2] + tiredValue(j);                }else{                    dp[i][j] = min(dp[i][j-1], dp[i-1][j-2] + tiredValue(j));                }            }        printf("%d\n", dp[k][n]);    }    return 0;}/**************************************************************    Problem: 1452    User: zhengx142857    Language: C++    Result: Accepted    Time:30 ms    Memory:8856 kb****************************************************************/

这里有个问题,我之前一直弄错,因为一共是k对物品,只需要算出前面重量最小的2*k个物品就可以了,然后一直WA。 后来想想发现不对,一是显然疲劳度是和重量的差值有关的,而不是重量的大小。按重量排序的目的是为了找到相邻重量,而不是为了找到小的重量,最优解很可能是选择了若干次重量很大的但是重量差小的一对物品。二是如果这样做那么直接求出前k对的疲劳度就完了,和动态规划就没关系了,是贪心,这样贪心的结果不一定是最优解。

总结:

  1. 对动态规划的状态转移与每次决策还需要进一步学习
  2. 推导转移方程时,先要确定每次有几种决策,然后手工模拟几次,对求出转移方程有很大帮助。
0 0
原创粉丝点击