题目大意:非诚勿扰里的男嘉宾们,在等待着上台展示,他们每个人都有一个屌丝值D,每个人都会因为等待上场而产生不悦,第K个男嘉宾的不悦程度是(K-1)*D,而主持人可以以某种方式改变他们上场的顺序。他可以暂时地把某人领到一个阴暗的地方,让他晚点上场,但是由于空间过于狭窄,最先进去的人只能最后出来。要求出他们的不悦程度的最小总和。
思路如下:这题看着题目,看着看着,脑子里就蹦出一个栈(先进后出的特性),感觉找到了思路,用数据结构啊?但是想着想着感觉不对头,发现被坑了,什么见鬼的栈,这题还是动态规划啊~!这题我开了三个数组,每个数组代表的含义先写在代码里数组声明后面。
分析这道题,只要清楚,对于给定的dp[i][j]只需考虑开头的男嘉宾需不需要往后跑即可,最终从最小dp[i][i+1](只有两人)扩展到整个dp[1][n]即可。当开头需要往后跑到第k个位置,只要明白这个变化过程,那么就能得出:
状态转移方程:dp[i][j]=Math.min(dp[i][j], dp[i+1][k]+dp[k+1][j]+(k-i+1)*(sum[j]-sum[k])+a[i]*[k-i])(i<=k<=j)
因为dp[i][j]是由dp[i+1][j]所确定的,由dp[i+1][j]扩展成为dp[i][j]区别只是开头多了一个人,这个人决定插入中间那个位置,所以(sum[j]-sum[k])是一撮人由前状态变为后状态位置不变,但是要多等(k-i+1)个人:((k-i+1)*(sum[j]-sum[k])),前面的没变化。而a[i]*[k-i]是排头跑到第k个位置后产生的屌丝值。这里似乎叙述的有点凌乱,数组小标整理地不够好~
AC代码:
import java.util.Scanner;
public class Main
{
static Scanner scan=new Scanner(System.in);
public static void main(String[] args)
{
int t=scan.nextInt();
int cas=0;
while(t-->0)
{
int n=scan.nextInt();
int a[]=new int[n+1];//接受数据
int sum[]=new int[n+1];//记总和,sum[i]即前 i 个人的屌丝值的总和
int dp[][]=new int[n+2][n+2];//dp[i][j]表示以第 i 个人为排头,第 j 个人为末尾(即此时排头者(i)的屌丝值是(1-1)*D),通常数组我喜欢动态的开,这里本来是开n+1的,但是后面有个地方会超出一位
for(int i=1; i<=n; i++)
{
a[i]=scan.nextInt();
sum[i]=sum[i-1]+a[i];//初始化sum
for(int j=1; j+i<=n; j++)
dp[j][j+i]=0x7fffffff;//初始化dp,不过dp[i][i]都是零(无需)
}
for(int i=1; i<=n; i++) //强迫症,本来判断条件是i<n的,加个"="没用,纯粹是为了好看。。。。
for(int j=1; j+i<=n; j++)
for(int k=j; k<=j+i; k++)
dp[j][j+i]=Math.min(dp[j][j+i], dp[j+1][k]+dp[k+1][j+i]+(k-j+1)*(sum[j+i]-sum[k])+a[j]*(k-j));
System.out.println("Case #"+(++cas)+": "+dp[1][n]);
}
}
}