附加题 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;
}

  

posted @ 2012-12-02 21:34  E_star  阅读(172)  评论(0编辑  收藏  举报