题意:
n个男孩排成队登台,每个男孩有一个屌丝值Di
若某男孩第k个登台 ,他将产生Di*(k-1)的愤怒值
要求所有男孩愤怒值最小
可以做的操作是将男孩丢进栈里面改变登台顺序(栈满足先进后出 )
分析:
dp[i][j]表示区间[i,j]所有男孩的最小愤怒值
if (i == j) return 0;//第一个登台愤怒值为0
状态转移:
首先明确的是区间[i,j]内共有j-i+1个人 (只考虑这j-i+1个人)
当从区间[i+1,j]到 区间[i,j],加入了第i个人
考虑第i个人第几个登台,假设他第k个登台
原本在[i,j]区间里面,他应该第一个登台
现在第k个登台,表示他后面k-1个人先于他登台(因为栈的特性)
所以现在顺序是[i+1,(i+1)+(k-1)-1]登台,i第k个登台,
后面的[(i+1)+(k-1),j]再登台
但是状态转移并不是dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j]
因为dp[(i+1)+(k-1)][j]是指这些人按从1开始的顺序登台
而实际上后面区间[(i+1)+(k-1),j]的登台是从k+1到(j-i+1)的顺序
其实只需再加上 k*∑a[l] ((i+1)+(k-1) <= l <= j)
也就是说,区间 [(i+1)+(k-1),j]里面每个人的登台都往后推了k位
所以最后的状态转移方程是:dp[i][j] = min
dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j] + k * sum[(i+1)+(k-1)][j]
其中sum[l][r]表示区间[l,r]的人的屌丝值之和
#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
题意:
n个男孩排成队登台,每个男孩有一个屌丝值Di
若某男孩第k个登台 ,他将产生Di*(k-1)的愤怒值
要求所有男孩愤怒值最小
可以做的操作是将男孩丢进栈里面改变登台顺序(栈满足先进后出 )
分析:
dp[i][j]表示区间[i,j]所有男孩的最小愤怒值
if (i == j) return 0;//第一个登台愤怒值为0
状态转移:
首先明确的是区间[i,j]内共有j-i+1个人 (只考虑这j-i+1个人)
当从区间[i+1,j]到 区间[i,j],加入了第i个人
考虑第i个人第几个登台,假设他第k个登台
原本在[i,j]区间里面,他应该第一个登台
现在第k个登台,表示他后面k-1个人先于他登台(因为栈的特性)
所以现在顺序是[i+1,(i+1)+(k-1)-1]登台,i第k个登台,
后面的[(i+1)+(k-1),j]再登台
但是状态转移并不是dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j]
因为dp[(i+1)+(k-1)][j]是指这些人按从1开始的顺序登台
而实际上后面区间[(i+1)+(k-1),j]的登台是从k+1到(j-i+1)的顺序
其实只需再加上 k*∑a[l] ((i+1)+(k-1) <= l <= j)
也就是说,区间 [(i+1)+(k-1),j]里面每个人的登台都往后推了k位
所以最后的状态转移方程是:dp[i][j] = min
dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j] + k * sum[(i+1)+(k-1)][j]
其中sum[l][r]表示区间[l,r]的人的屌丝值之和
*/
const int N = 111;
int dp[N][N];
int sum[N];//其实一维数组也可以就不用二维数组了sum[r] - sum[l-1]代替sum[l][r]
int a[N];
int DP(int i,int j)
{
if (i >= j) return 0;
if (~dp[i][j]) return dp[i][j];
//枚举第i个人的登台顺序
dp[i][j] = DP(i+1,j) + a[i]*((j-i+1)-1);//最后一个登台
for (int k = 1;k < j-i+1;++k)
{
int mid = (i+1)+(k-1)-1;
dp[i][j] = min(dp[i][j],DP(i+1,mid)+a[i]*(k-1)+DP(mid+1,j)+k*(sum[j]-sum[mid])) ;
}
return dp[i][j];
}
int main()
{
int T;Sint(T);int kas = 0;
while (T--)
{
int n;Sint(n);
for (int i = 1;i <= n;++i)
{
Sint(a[i]);
sum[i] = sum[i-1]+a[i];
}
mem(dp,-1);
printf("Case #%d: %d\n",++kas,DP(1,n));
}
return 0;
}