TKO 6-7DP入门之搬寝室

搬寝室

涉及数学公式的证明以及一些贪心思想:

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

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long int ob[2001];
long long int dp[2001][2001];
const long long int max1=0x3f3f3f3f;
int main()
{
    int n,k;
    while(cin>>n>>k)
    {
      //  memset(dp,max1,sizeof(dp));  memset 最好用来赋值0或-1,不然会出错。
        memset(ob,0,sizeof(ob));
        for(int i=1;i<=n;i++)
            cin>>ob[i];
        sort(ob+1,ob+n+1);
        ob[0]=0;
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
            {
                dp[i][j]=max1;
            }
        }
        for(int i=0;i<=n;i++) dp[i][0]=0;
        for(int i=2;i<=n+1;i++)
        {
            for(int j=1;j<=k;j++)
                dp[i][j]=min(dp[i-2][j-1]+(ob[i-1]-ob[i])*(ob[i-1]-ob[i]),dp[i-1][j]);
        }
       /* for(int i=0;i<n+1;i++)   
       遇到不会的题目,就按照他的状态转移方程操作后输出dp数组,
       来研究状态转移方程的实现过程。
        {

            for(int j=0;j<n+1;j++)
            {
                cout<<dp[i][j]<<' ';
            }
            cout<<endl;
        }*/
        cout<<dp[n][k]<<endl;
    }
		然而,还有更加神仙的代码,比如下面这种,
		二维化一维,恐怖如斯:
#include <bits/stdc++.h>
using namespace std;
#define MXN 2010
#define INF 0x7fffffff
int n, k, a[MXN], dp[MXN];
int main() {
    while (cin >> n >> k) {
        for(int i = 1; i <= n; i++) scanf("%d", a+i);
		sort(a+1, a+n+1);
		//为涉及到的数组元素均为0
		memset(dp, 0, sizeof dp);
		dp[0] = INF;
		
		for(int i = 1; i <= k; i++){
			for(int j = 1; j <= n+2-2*k; j++){
				dp[j] = min(dp[j-1], dp[j]+(a[2*i-2+j]-a[2*i+j-1])*(a[2*i-2+j]-a[2*i+j-1]));
			}
		}
/*	表达的意思是:在寻找第i组选择时(即搬运第i趟物品时),
依次在可能的数据范围(2*i-2+j)内依次查找并比较最小值,
并被dp数组记录。

	dp数组特点:每当重新开始新的一趟时,
总会记录着上一次取到物品的最小值;
而且这个程序的dp数组只会记录最后一趟搬运后所存储的能量值,
并不会记录所有趟数的能量值,
因为dp数组在不断比较运算的过程中属不断被覆盖!!
在编程的时候,看起来dp数组是竖直覆盖的,
其实根据a数组的下标2*i-2+j,
可以推断出dp在比较和存储的过程中,
其实是通过i的改变将其不同区域内的下标不同的值平移到一起,
造成了竖直覆盖的假象,毕竟i在不断前进嘛!
所以说白了,在原图上看,其实dp数组的范围是斜向对应的。
*/
        cout << dp[n+2-2*k] << endl;
    }
    return 0;
}

https://blog.csdn.net/jpphy0/article/details/116424360?spm=1001.2014.3001.5501

这是老师对于搬寝室问题的独到理解,重点是节约了运算时间,
和储存空间,是算法得到了极大的优化。

上一篇:vue数据响应式原理 - 数组的响应式


下一篇:php指定url生成二维码