HDOJ 4283 You Are the One(经典区间dp)

题意:

给定一个序列,序列内的人有屌丝值Di,第i个人如果是第k个出场,那么他的屌丝值为Di * (k-1),  但是导演可以通过一个栈来调整序列里面人的出场顺序。

求一个出场序列使总屌丝值最小。

思路:

题意一开始有点儿混乱,但是最后要明白一点:导演对于这个出场顺序的影响只是一定程度上的。比如说:

1. 第一个人第k个出场

2. 那么要求2~k的人都要在第一个人前面出场

3. k+1~n的人都要在k以后出场

明白了上面的过程,就可以定义区间dp[i, j]表示区间[i, j]在相对于i为起点情况下i在第k个出场的最小屌丝总值。

1. dp[i, j] = (k - i) * Di + dp[i+1, k]

2. dp[i, j] += (k + 1 - i) * (sum[j] - sum[k]) + dp[k+1, j]

注意一切都是相对的,直到dp[1, n]的时候就是相对于1了,dp[1, n]就是所要求的值。


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;

const int MAXN = 110;
int a[MAXN], sum[MAXN];
int dp[MAXN][MAXN];


int solve(int n)
{
    for (int i = 1; i <= n; ++i)
        dp[i][i] = dp[i+1][i] = 0;

    for (int p = 2; p <= n; ++p)
        for (int i = 1, j = p; j <= n; ++i, ++j)
        {
            dp[i][j] = INT_MAX;
            for (int k = i; k <= j; ++k)
                dp[i][j] = min(dp[i+1][k] + dp[k+1][j] + (k-i)*a[i] + (k-i+1)*(sum[j]-sum[k]), dp[i][j]);
        }
    return dp[1][n];
}

int main()
{
    int cases, t = 0;
    char b[20];
    scanf("%d", &cases);
    while (cases--)
    {
        int n;
        scanf("%d", &n);
        sum[0] = 0;
        for (int i = 1; i <= n; ++i)
            scanf("%d", &a[i]), sum[i] = a[i] + sum[i-1];

        printf("Case #%d: %d\n", ++t, solve(n));
    }
    return 0;
}

 

posted @ 2012-12-10 11:17  kedebug  阅读(416)  评论(0编辑  收藏  举报