附加题 DP题目

http://acm.hdu.edu.cn/diy/contest_showproblem.php?cid=18068&pid=1008

题意:

给你n个数a[n],求从中顺序的选出k个数b[k],这k个数分别进行如下操作,sum = b[1]*1 + b[2]*2 + b[3]*3 + ...... + b[k]*k 求使得sum最小。才开始von给我说可能需要单调队列优化,我看看了,用单调队列做了一下。提交不对,一看状态转移方程推错了。囧....后来一看这不是一个很典型的状态转移方程式吗。以前做过类似的题目

dp[i][j] = min(dp[i - 1][j],dp[i - 1][j - 1] + a[i]*j)

写了个二维的,结果直接MLE,这不科学,计算10^7次不会MLE。于是就去优化。我们可以看出i由i-1推出,所以我们可以把j反向取,把i这一维省去。有点01背包优化的感觉。

注意一下数据范围:

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll __int64

#define inf 0x7f7f7f7f

#define MOD 1073741824

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 500007

#define N 10007

#define K 1007

using namespace std;

//freopen("din.txt","r",stdin);



ll a[N];

ll dp[K];



int main()

{

    //freopen("din.txt","r",stdin);

    int i,j;

    int n,k;

    while (~scanf("%d%d",&n,&k))

    {

        for (i = 1; i <= n; ++i) scanf("%I64d",&a[i]);

        for (i = 0; i <= k; ++i) dp[i] = inf;

        dp[1] = a[1]; dp[0] = 0;

        for (i = 2; i <= n; ++i)

        {

            for (j = min(i,k); j >= 1; --j)

            {

                dp[j] = min(dp[j],dp[j - 1] + a[i]*j);

            }



            /*

            for (j = 1; j < i && j <= k; ++j)

            {

                dp[i][j] = min(dp[i - 1][j],dp[i - 1][j - 1] + a[i]*j);

            }

            */

        }

        printf("%I64d\n",dp[k]);

    }

    return 0;

}

  

你可能感兴趣的:(dp)