版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41661919/article/details/82194417
题目大意:
给出n个排好队的屌丝,每个屌丝有一个权值,当第i个屌丝在第k个位置时,将产生一个生气值=(k-1)*权值,他们之间的顺序可以通过一个栈调整,问怎样排序能使产生的生气值之和最小。
分析:
首先,这个栈有什么用呢?它可以把一个屌丝调整到该屌丝之后的所有位置上去。
1、状态:区间dp,二维数组表示区间。
2、递推关系:入门级专题训练中难点一般在于花费的确定,(ps:说到底好像是断点的含义难想?):
区间长度为1时(初始值处理),dp值为零;
区间长度为2时(初始值处理),dp值取决于两个屌丝的相对位置,好像看不出什么规律,,,
区间长度为3时(找规律/递推关系),dp值取决于这三个屌丝的顺序,考虑这三个屌丝的顺序怎么由len=2时递推过来呢,当第一个屌丝在第一时,另外两个屌丝有两种排列方式,所以,,,总共2*3=6种排列方式。也就是说,我们枚举第一个屌丝的位置,便可以产生区间长度为3时的某(指定区间状态)的所有排列方式的值,然后指定区间状态选择排列中的最优值即可,,,
区间长度为4时(验证递推关系是否可推广),是不是也用同样的递推关系呢?显然,通过枚举第一个屌丝的位置我们仍能得到区间长度为4的指定区间时所有排列方式,即状态,同样,对于指定区间,选择最优值即可。
,,,,,,
可得状态转移方程:
(k-i)和(k-i+1)的关系可以推算一下,直接想必然不好想。
注意:dp[i][j]所代表的只是指定区间的最优解,即i为第一个元素,,,所以,花费那么写。。。
3、目标状态:dp[1][n];
AC代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define ll long long
#define INF 0x7fffffff
using namespace std;
ll dp[110][110];
ll sum[120];
ll a[120];
int main()
{
ll t,n;
cin>>t;
ll cas=0;
while(t--)
{
cin>>n;
memset(sum,0,sizeof(sum));
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
for(ll i=1;i<=n;++i)cin>>a[i],sum[i]+=sum[i-1]+a[i];
for(ll i=1;i<=n;++i)
for(ll j=i;j<=n;++j)
{
if(i==j)dp[i][j]=0;
}
for(ll len=2;len<=n;++len)
{
for(ll i=1;i<=n&&i+len-1<=n;++i)
{
ll j=i+len-1;
dp[i][j]=dp[i+1][j]+sum[j]-sum[i];//别忘了i在第一个时的情况;
for(ll k=i+1;k<=j;++k)//一般来说,断点的范围及含义要好好考虑;
{
dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]+(k-i)*a[i]+(sum[j]-sum[k])*(k-i+1));
}
}
}
cout<<"Case #"<<++cas<<": "<<dp[1][n]<<endl;
}
return 0;
}
The end;