You Are the One
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1381 Accepted Submission(s): 652
Problem Description
The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there are n boys enrolling in. At the beginning, the n boys stand in a row and go to the stage one by one. However, the director suddenly knows that very boy has a value of diaosi D, if the boy is k-th one go to the stage, the unhappiness of him will be (k-1)*D, because he has to wait for (k-1) people. Luckily, there is a dark room in the Small hall, so the director can put the boy into the dark room temporarily and let the boys behind his go to stage before him. For the dark room is very narrow, the boy who first get into dark room has to leave last. The director wants to change the order of boys by the dark room, so the summary of unhappiness will be least. Can you help him?
Input
The first line contains a single integer T, the number of test cases. For each case, the first line is n (0 < n <= 100)
The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)
Output
For each test case, output the least summary of unhappiness .
Sample Input
2 5 1 2 3 4 5 5 5 4 3 2 2
Sample Output
Case #1: 20 Case #2: 24
Source
2012 ACM/ICPC Asia Regional Tianjin Online
算法分析:
题意:
思路:
本题是用dp来模拟栈,通过将数列划分为两个区间,来代替栈内和站外的区别。
举个例子
有5个人事先排好顺序 1,2,3,4,5
入栈的时候,1入完2入,2入完3入,如果我要第1个人第3个出场,那么入栈出栈顺序是这样的:
1入,2入,3入,3出,2出,1出(到此第一个人就是第3个出场啦,很明显第2,3号人要在1先出,而4,5要在1后出)
dp[i][j]表示区间[i,j]的最小总不开心值
对于第i个人,在本数列(只有i ~ j)他可以1 ~ j - i + 1个出场,考虑i号人第K个上场。则出场方式只有三种:
1、人i第K个出场之前的情况:在区间内i号人本来是第一个出场,现在是第K个出场(相当于进栈),说明原本在人i之后的K-2个人(所以后面的下标是i + 1,而且最后不带K本身)是率先上场的,那么就出现了一个子问题dp[i+1][i+k-1]表示在第i个人之前上场的k-1个人的情况;
2、人i第K个出场之时的情况:由于i号人是第K个上场的,那么其对愤怒值之和的贡献是num[i]*(K-1);
3、人i第K个出场之后的情况:其余的人是从第K+1个开始出场,也就是一个子问题dp[i+k][j],对于第i个人后面上场的j-(i+k)个人来说每个人的都已经等待了k次,加上k(sum[j]-sum[i+k-1]);
然后就开始模拟栈,第一层for划分出一个大区间,然后第二层for从大区间中划分出小区间,这个小区间就是i ~ j的区间。然后插入K作为栈内和栈外的临界点调用状态方程。最为精妙的是通过外面两次区间遍历,真的将一个数列中各种区间的表示形式都表现了出来,
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=105;
int a[maxn],sum[maxn],f[maxn][maxn];
int main()
{
int T,kase=1;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
sum[i]=sum[i-1]+a[i];
f[i][i]=0;
}
for(int l=2;l<=n;l++)
for(int i=1;i+l-1<=n;i++)
{
int j=i+l-1;
f[i][j]=INF;
for(int k=1;k<=j-i+1;k++)
f[i][j]=min(f[i][j],(k-1)*a[i]+f[i+1][i+k-1]+f[i+k][j]+k*(sum[j]-sum[i+k-1]));
}
printf("Case #%d: %d\n",kase++,f[1][n]);
}
return 0;
}