题目
n(n<=100)个男孩排队进屋子,第i个人有一个系数Di,
如果第i个人是第k个人进去的,他的不快乐指数是(k-1)*Di
有一个栈结构的屋子可以临时容纳男孩,先进后出的结构,
问如何安排出栈顺序,使得n个人的不快乐指数之和最小,
输出不快乐指数之和
思路来源
https://blog.csdn.net/acm_cxlove/article/details/7964594
题解
dp[l][r]表示只考虑[l,r]的人时的最小不快乐指数之和,此处枚举左端点是第几个出栈的,
如果l是第1个出栈的,[l+1,r]只能在其出栈后入栈再出
如果l是第k个出栈的,一定是l入栈,[l+1,l+k-1]先入栈再出栈,l出栈,[l+k,r]先入栈再出栈
即l的出栈位置可以将序列划分成两段,
转移时首先是拆成两段的代价和,此外还要加上前面的人对后面的人出栈的不快乐指数增量的影响
也可以向考虑卡特兰数推导公式那样做吧,枚举哪一个位置x的值是这一段最后出栈的,
则x前面的先入栈后出栈(不然会被压在底下),x入栈,x后面的入栈再出栈,x出栈,仍分成三段
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
int t,n,a[N],sum[N],dp[N][N];
int cal(int l,int r){
if(l>r)return 0;
return sum[r]-sum[l-1];
}
int main(){
scanf("%d",&t);
for(int ca=1;ca<=t;++ca){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
dp[i][i]=0;
}
for(int len=2;len<=n;++len){
for(int l=1;l+len-1<=n;++l){
int r=l+len-1;
dp[l][r]=dp[l+1][r]+cal(l+1,r);//l是[l,r]这一段第一个出栈的
for(int k=2;k<=len;++k){//l是[l,r]这一段第几个出栈的,特别地,k==len时最后一个出栈dp[l+k][r]=cal(l+k,r)=0
dp[l][r]=min(dp[l][r],dp[l+1][l+k-1]+dp[l+k][r]+(k-1)*a[l]+k*cal(l+k,r));//k-1个人位于a[l]前,k个人位于[l+k,r]这段前
}
}
}
printf("Case #%d: %d\n",ca,dp[1][n]);
}
return 0;
}