2 5 1 2 3 4 5 5 5 4 3 2 2
Case #1: 20 Case #2: 24
比较经典的一道区间DP题目,思路都是对区间进行分割。
dp[i][j]表示单独考虑从第i个人到第j个人这段区间的最小不满意度,单独考虑意味着不考虑i之前的人,即把第i个人当做第一个人。
首先考虑栈对于队列中元素的影响。我们假设不管需不需要调换顺序,每个人出队列之后必须进栈,然后再从栈中出去。这样假设第i个人从第一个出去变成第k个出去,那么
[i+1,i+k-1]这段区间的人必须比i先出去,而[i+k,j]这个区间的人必须在k后面出去,那么
dp[i][j]就会由三部分组成,
dp[i+1][i+k-1],由于第i+1个人本来就是第一个出去,所以不变
(k-1)*ds[i],第i个人变成第k个出去的不满意度
dp[i+k][j]+(sum[j]-sum[i+k-1])*k,第i+k个人原本是第一个出去,现在
变成第k+1个出去,因此他后面所有的人的不满意度都要增加
因此,最终的状态转移方程就是
dp[i][j]=min(dp[i][j],dp[i+1][i+k-1]+dp[i+k][j]+ds[i]*(k-1)+(sum[j]-sum[i+k-1])*k)
/***************** 功能:元素在队列中,若想出队列,需要先进栈,然后出栈,每个元素出栈的时刻会影响其不满意度,求某个策略,使 不满意度最小。 参数:元素的不满意参数 返回值:最小的不满意度 *****************/ #include <iostream> #include <stdio.h> #include <string.h> #define maxn 110 #define INF 200000000 using namespace std; int ds[maxn]; int dp[maxn][maxn]; int sum[maxn]; void init(int n) { int i,j; for(sum[0]=ds[0],i=1;i<n;i++) { sum[i]=sum[i-1]+ds[i]; } memset(dp,0,sizeof(dp)); for(i=0;i<n;i++) for(j=i+1;j<n;j++) dp[i][j]=INF; } int solve(int n) { int i,j,k; int len; int tmp; init(n); for(len=1;len<=n;len++) { for(i=0;i<=n-len;i++) { j=i+len-1; for(k=1;k<=len;k++) { tmp=min(dp[i][j],dp[i+1][i+k-1]+dp[i+k][j]+ds[i]*(k-1)+(sum[j]-sum[i+k-1])*k); if(tmp<dp[i][j]) dp[i][j]=tmp; } } } return dp[0][n-1]; } int main() { int T,cas=0,n; int i; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&ds[i]); printf("Case #%d: %d\n",++cas,solve(n)); } return 0; } /*********************** 输入: 2 5 1 2 3 4 5 5 5 4 3 2 2 输出: Case #1: 20 Case #2: 24 ***********************/