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;

}

 

你可能感兴趣的:(one)