题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4283
题意是现在一场表演,有n个人排成一队要上台表演,由每个人有一个权值D[i],第i个人上场时总的答案要加上(i-1)*D[i];
然后现在有一个栈供我们进行调整出场顺序,问如何调整使得答案最小,输出这个答案;
我们分析一下可以看出来,如果可以随便调整的话,那么肯定就是把值从大到小放置,但是栈的话有一些情况是没办法把顺序
调整到刚好从大到小的。一开始想了一个dp[i][j]代表第i个数的时候栈里有j个数字的最小答案,但是发现好像不方便转移,因为要记一
下当时栈里j个数是哪些数字。后来发现栈的本质就是把顺序做出一些调整那应该就是一个区间DP了,然后研究一下栈的性质,发现
假设初始序列是1 2 3 4 5 6 ……那么经过栈调整之后的序列,对于每一个数字,假设该数字后面没有比他大的数字的话,那么从
这个数字往后的序列必须是逆序的,比如1 2 3 4 经过调整后1 4 2 3 这种情况是不可能出现的,必须是1 4 3 2
有了这些性质之后我们就可以进行转移了,记dp[i][j]为从i到j的调整后的最小答案,这么进行转移时枚举k,我们把k到j这个区间逆序后,插到
i到k - 1后面(i到k-1无论顺序变成什么样这样转移都是符合栈规律的);还有就是把i到k-1逆序后插到 k到 j后面(同样也是符合栈规律的);
还有一种情况就是k到j这个区间放在i到k-1这个区间后面,(这个无论两个区间怎么转移都是合法的,其实这个才是最关键的方程);
那么第三个情况的方程就是
dp[i][j] = min (dp[i][j] ,dp[i][k] + dp[k+1][j] + (sum[j] - sum[k]) *(k - i + 1) );
其实后来我发现第三个方程已经囊括了前面两种情况啊!!!!这个东西我一开始就想到了,但是被我否认了,于是用了两三个小时想出
上面的两个方程,再由前面两个再验证了这个方程,真的好气啊!!!!
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define LONG long long
const int INF=0x3f3f3f3f;
const LONG MOD=1e9+7;
const double PI=acos(-1.0);
#define clr0(x) memset(x,0,sizeof x)
#define clrI(x) memset(x,-1,sizeof(x))
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
LONG dp[150][150];
LONG D[150];
LONG sum[150];
int main()
{
// freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","r",stdin);
//freopen("C:\\Users\\ZhangYuyang\\Desktop\\out.txt","w",stdout);
int T;
int ca = 1;
cin>>T;
while(T--)
{
int n ;
cin>>n;
sum[0 ] = 0;
for(int i = 1 ;i <= n ; ++ i)
scanf("%lld",&D[i]) ,
sum[i] = D[i] + sum[i - 1];
clr1(dp);
for(int i = 1; i<= n ;++ i)
dp[i][i] = 0 ;
for( int l = 1; l <= n ;++ l)
{
for(int i = 1; i < n ;++ i)
{
int j = i + l ;
if(j > n) break ;
for(int k = i; k <= j ;++ k)
{
if(k == j)
dp[i][j] = min(dp[i][j] , dp[i][j-1] + D[j] * l);
if(k < j)
{
LONG tmp = 0;
LONG temp = l ;
for(int t = i ; t <= k ;++ t)
tmp += D[t] * (temp -- );
dp[i][j] = min(dp[i][j] , dp[k + 1][j] + tmp);
dp[i][j] = min (dp[i][j] ,dp[i][k] + dp[k+1][j] + (sum[j] - sum[k]) *(LONG )(k - i + 1) );
}
}
}
}
printf("Case #%d: ",ca++);
cout<<dp[1][n]<<endl;
}
}