题意:
给定 N N 个人,编号分别为,按顺序进栈,随意选择出栈顺序,每个编号为 i i 的人在第个出栈时,其花费为: v[i]∗(k−1) v [ i ] ∗ ( k − 1 ) . 求所有可能的出栈顺序中,总花费的最小值?
思路:
非常好的DP题目,一旦想到状态定义后,后面的东西就简单了。
一开始,由于这个跟卡特兰计数非常相关,我自己想的定义 dp[i][j][k] d p [ i ] [ j ] [ k ] 为目前进栈了 i i 个出栈了个,栈顶元素为 k k 情况下最小的花费值,有个问题就是必须得维护栈的状态,因为进行出栈操作后,就不知道次栈顶的元素是哪个了?维护栈的状态行不通,GG。
~~
正确的思路是区间DP:考虑区间第一个人是第几个出栈,由他产生的花费。记录为所有人不同出栈顺序中最小的花费,那么设第一个人第 k k 个出场,则:内这 k−1 k − 1 个人一定在第一个人之前出场,且 [k+1,n] [ k + 1 , n ] 这些人一定在第一个人之后出场。然后考虑第一个人第 k k 个出场带来的花费:
1、第一部分,自身花费:
2、第二部分,给后面出场的人带来的花费: k∗(sum[n]−sum[k]) 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;
}