HDU 4283 You Are the One

题意:给定n(n<=100)个人,每个人有个固定的屌丝值D。 起初这些人是站成一行,当第i个人第j个去面试的时候他的值是 Di*j。
要求所有人面试之后 这些值加起来是最小的。
队伍站成一行(其实就是个队列), 队列头部里的人可以到一个小黑屋子里,先进黑屋子的必须后出来(相当于一个栈)。
然后队首的人可以选择进黑屋或者去面试,黑屋里的也可以选择去面试。 要求最少的Di*j 值之和。
===============================================================================
题目分析:
这个题目也是想了好久, 最初看的时候一直认为DP式子是:
 dp[L][R] = min( dp[L][R], dp[L+1][k] + a[L]*k + dp[k+1][R] )   (L<= k <= R)
起初是这样认为的。但是后来发现这样的子结构其实是不对的, 我们对其位置交换的时候会出现一些问题。
后来看看题解发现问题所在:
子结构其实应该是 DP[L][R] 代表区间L,R之间 (0,R-L)次选择的方式。并不应该是(L,R)的选择方式。
(sum[i] 代表从a[0]加到a[i])
故得到一个递推式: dp[L][R] = min(dp[L][R], dp[L+1][L+k] + a[L]*k + dp[L+k+1][R] + (sum[R]-sum[L+k])*(k+1) );
关键是:为什么要加上他 (sum[R]-sum[L+k])*(k+1) 。 前面我们  dp[L+1][L+k] 这个区间不用加是因为我们最先选的就是这个区间,所以不加。
至于区间dp[L+k+1][R],  因为我们的算的是区间(L+k+1,R)的第k次选择的结果,而k是(0,R-L-k-1)的。因此我们少加上了 (sum[R]-sum[L+k])*(k+1) 。
=========================================================================================
 
 
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL INF = 0xfffffff;
const LL maxn = 105;
int dp[maxn][maxn], a[maxn], sum[maxn];

int DFS(int L,int R)
{
    if(dp[L][R])
        return dp[L][R];
    if(L > R)
        return 0;

    dp[L][R] = INF;
    for(int k=0; k<=R-L; k++)///在区间【L,R】内,这个数该如何去选择
    {///假设第L个数字,要在第K次选择
        dp[L][R] = min(dp[L][R], DFS(L+1, L+k) + a[L]*k + DFS(L+k+1,R) + (sum[R]-sum[L+k])*(k+1) );
    }
   // printf("dp[%d][%d]:%d\n",L, R, dp[L][R]);
    return dp[L][R];
}
/*
第L个元素第k个出场拍,
*/
int main()
{
    int T, n, cas = 1;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        memset(dp, 0, sizeof(dp));
        for(int i=0; i<n; i++)
            scanf("%d", &a[i]);
        sum[0] = a[0];

        for(int i=1; i<n; i++)
            sum[i] = sum[i-1] + a[i];

        printf("Case #%d: %d\n",cas ++, DFS(0, n-1));
    }
    return 0;
}

/*
2
2
4 5


2
5
1 2 3 4 5

5
5 4 3 2 2
*/

 

posted @ 2015-09-25 15:39  向前走丶不回首  阅读(182)  评论(0编辑  收藏  举报