HDU1421:搬寝室(DP)

搬寝室
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 33811 Accepted Submission(s): 11570

Problem Description
搬寝室是很累的,xhd深有体会.时间追述2006年7月9号,那天xhd迫于无奈要从27号楼搬到3号楼,因为10号要封楼了.看着寝室里的n件物品,xhd开始发呆,因为n是一个小于2000的整数,实在是太多了,于是xhd决定随便搬2k件过去就行了.但还是会很累,因为2k也不小是一个不大于n的整数.幸运的是xhd根据多年的搬东西的经验发现每搬一次的疲劳度是和左右手的物品的重量差的平方成正比(这里补充一句,xhd每次搬两件东西,左手一件右手一件).例如xhd左手拿重量为3的物品,右手拿重量为6的物品,则他搬完这次的疲劳度为(6-3)^2 = 9.现在可怜的xhd希望知道搬完这2*k件物品后的最佳状态是怎样的(也就是最低的疲劳度),请告诉他吧.

Input
每组输入数据有两行,第一行有两个数n,k(2<=2*k<=n<2000).第二行有n个整数分别表示n件物品的重量(重量是一个小于2^15的正整数).

Output
对应每组输入数据,输出数据只有一个表示他的最少的疲劳度,每个一行.

Sample Input
2 1
1 3

Sample Output
4

Author
xhd

我一开始的做法是单独把第一列先给填上去,因为除了第一列,后面的几列填的时候都要和前面的一列有联系。

  • 第一列:
    1.列首元素 :因为上面没有元素可以跟他比较大小,所以直接把(a[i] - a[i-1]) * (a[i] - a[i-1]);填入。
    2.非列首元素:因为要和上面的元素比较取最小值,所以 = std::min(dp[i-1][1],(a[i] - a[i-1]) * (a[i] - a[i-1])};

  • 非第一列:
    1.列首元素:因为上面没有元素可以跟他比较大小,所以dp[i][j] = (a[i] - a[i-1]) * (a[i] - a[i-1]) + dp[i-2][j-1];
    2.非列首元素,因为要和上面的元素比较取最小值,所以dp[i][j] = std::min(dp[i-1][j](这是不选的情况),(a[i] - a[i-1]) * (a[i] - a[i-1]) + dp[i-2][j-1]);

下面是我自己举得一个例子:
6 2
1 3 6 7 9 10(顺序任意)
在这里插入图片描述
附上ac代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <queue>
#include <cmath>
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
typedef long long int ll;
ll a[3000];
ll dp[2010][1005];  //wa就开ll
int main() {
    int n,k;
    while(cin >> n >> k) { //每组输入数据
        a[0] = -INF; //让负INF永远填在a[0]的那个位置上
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        sort(a,a+n+1);
        memset(dp,0,sizeof(dp));
        for(int i = 2; i <= n; i++) {
            if(i == 2) {
                dp[2][1] = (a[2] - a[1]) * (a[2] - a[1]); //首元素
            } else {
                dp[i][1] = std::min(dp[i-1][1],(a[i] - a[i-1]) * (a[i] - a[i-1]));
            }
        }
        for(int j = 2; j <= k; j++) {
            dp[2*j][j] = dp[2*j-2][j-1] + (a[2*j] - a[2*j-1]) * (a[2*j] - a[2*j-1]); //首元素
            for(int i = 2*j + 1; i <= n; i++) {
                dp[i][j] = std::min(dp[i-1][j],dp[i-2][j-1] + (a[i] - a[i-1]) * (a[i] - a[i-1]));
            }
        }
//    for(int i = 0; i <= n; i++) {             自检用
//        for(int j = 0; j <= k; j++) {
//            cout << dp[i][j] << " ";
//        }
//        cout << endl;
//    }
        cout << dp[n][k] << endl;
    }
    return 0;
}

 


然后写完发现,反正dp数组的第0列全是0,加了没影响,干脆并在一起了,就比较简洁。
ac代码如下:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <queue>
#include <cmath>
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
typedef long long int ll;
ll a[3000];
ll dp[2010][1005];  //wa就开ll
int main() {
    int n,k;
    while(cin >> n >> k) { //每组输入数据
        a[0] = -INF; //让负INF永远填在a[0]的那个位置上
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        sort(a,a+n+1);
        memset(dp,0,sizeof(dp)); //把所有空格都填0
        for(int j = 1; j <= k; j++) {
            dp[2*j][j] = (a[2*j] - a[2*j-1]) * (a[2*j] - a[2*j-1]) + dp[2*j-2][j-1];
            for(int i = 2 * j + 1; i <= n; i++) {
                dp[i][j] = std::min(dp[i-1][j],dp[i-2][j-1] + (a[i] - a[i-1]) * (a[i] - a[i-1]));
            }
        }
//    for(int i = 0; i <= n; i++) {             自检用
//        for(int j = 0; j <= k; j++) {
//            cout << dp[i][j] << " ";
//        }
//        cout << endl;
//    }
        cout << dp[n][k] << endl;
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_43555854/article/details/86633448