HDU - 4283 You Are the One(区间DP)

题意:

给定 N 个人,编号分别为 1 N ,按顺序进栈,随意选择出栈顺序,每个编号为 i 的人在第 k 个出栈时,其花费为: v [ i ] ( k 1 ) . 求所有可能的出栈顺序中,总花费的最小值?

思路:

非常好的DP题目,一旦想到状态定义后,后面的东西就简单了。
一开始,由于这个跟卡特兰计数非常相关,我自己想的定义 d p [ i ] [ j ] [ k ] 为目前进栈了 i 个出栈了 j 个,栈顶元素为 k 情况下最小的花费值,有个问题就是必须得维护栈的状态,因为进行出栈操作后,就不知道次栈顶的元素是哪个了?维护栈的状态行不通,GG。
~~
正确的思路是区间DP:考虑区间第一个人是第几个出栈,由他产生的花费。记录 d p [ 1 ] [ n ] 为所有人不同出栈顺序中最小的花费,那么设第一个人第 k 个出场,则: [ 2 , k ] 内这 k 1 个人一定在第一个人之前出场,且 [ k + 1 , n ] 这些人一定在第一个人之后出场。然后考虑第一个人第 k 个出场带来的花费:
1、第一部分,自身花费: v [ 1 ] ( k 1 )
2、第二部分,给后面出场的人带来的花费: k ( s u m [ n ] s u m [ k ] ) (sum为前缀和)

代码:

#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x7fffffff
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
typedef vector<int> VI;
typedef map<int,int> MII;
const int N = 105, MOD = 7+1e9;
int n, d[N][N], sum[N];
int ds[105];
int dp(int L, int R) 
{
    if(L >= R) return 0;
    if(d[L][R] != -1) return d[L][R];
    d[L][R] = INF;
    for(int i = L; i <= R; i++)
    {
        d[L][R] = min(d[L][R], dp(L+1, i) + (i-L)*ds[L] + dp(i+1, R) + (sum[R]-sum[i]) * (i+1-L));
    }
    return d[L][R];
}
int kase;
int main() 
{
#ifdef YUUKILP
    freopen("in.txt", "r", stdin);
#endif
    int t; cin >> t;
    while(t--) 
    {
        memset(d, -1, sizeof(d));
        cin >> n;
        for(int i = 1; i <= n; i++)
        {  
            cin >> ds[i];
            sum[i] = sum[i-1] + ds[i];  
        }
        printf("Case #%d: %d\n", ++kase, dp(1, n));  
    }
    return 0;  
}  

猜你喜欢

转载自blog.csdn.net/u014686462/article/details/80680561