bzoj4897: [Thu Summer Camp2016]成绩单【区间dp】

Description

期末考试结束了,班主任L老师要将成绩单分发到每位同学手中。L老师共有n份成绩单,按照编号从1到n的顺序叠
放在桌子上,其中编号为i的成绩单分数为w_i。成绩单是按照批次发放的。发放成绩单时,L老师会从当前的一叠
成绩单中抽取连续的一段,让这些同学来领取自己的成绩单。当这批同学领取完毕后,L老师再从剩余的成绩单中
抽取连续的一段,供下一批同学领取。经过若干批次的领取后,成绩单将被全部发放到同学手中。然而,分发成绩
单是一件令人头痛的事情,一方面要照顾同学们的心理情绪,不能让分数相差太远的同学在同一批领取成绩单;另
一方面要考虑时间成本,尽量减少领取成绩单的批次数。对于一个分发成绩单的方案,我们定义其代价为:
这里写图片描述
其中,k是方案中分发成绩单的批次数,对于第i批分发的成绩单,〖max〗_i是最高分数,〖min〗_i是最低分数。
a,b是给定的评估参数。现在,请你帮助L老师找到代价最小的分发成绩单的方案,并将这个最小的代价告诉L老师
。当然,分发成绩单的批次数k是由你决定的。

Input

第一行包含一个正整数n,表示成绩单的数量。
第二行包含两个非负整数a,b,表示给定的评估参数。
第三行包含n个正整数w_i,表示第i张成绩单上的分数。

Output

仅一个正整数,表示最小的代价是多少。

Sample Input

10

3 1

7 10 9 10 6 7 10 7 1 2

Sample Output

15

【样例数据说明】

第1批:第2至4份成绩单,落差值为1,剩余成绩单为76710712;

第2批:第4份成绩单,落差值为0,剩余成绩单为767712;

第3批:第1至4份成绩单,落差值为1,剩余成绩单为12;

第4批:剩余的2份成绩单,落差值为1。

总代价为4×3+(1^2+0^2+1^2+1^2)×1=15。

HINT

n<=50, a<=100, b<=10, w_i<=1000

解题思路:

注意是从中间抽,否则就太简单了。
每次抽肯定是一个子序列,且中间的空都是被抽空了。
g[l][r] g [ l ] [ r ] 表示区间 [l,r] [ l , r ] 被抽空的最小代价, f[l][r][i][j] f [ l ] [ r ] [ i ] [ j ] 表示区间 [l,r] [ l , r ] 最后一抽最小值为 i i ,最大值为j,且强制要选 a[l],a[r] a [ l ] , a [ r ] 的最小代价,则。

f[l][r][i][j]+g[r+1][R1]f[l][R][min(i,a[R])][max(j,a[R])]) f [ l ] [ r ] [ i ] [ j ] + g [ r + 1 ] [ R − 1 ] → f [ l ] [ R ] [ m i n ( i , a [ R ] ) ] [ m a x ( j , a [ R ] ) ] )
f[l][r][i][j]+g[r+1][R]+A+B(ji)2g[l][R] f [ l ] [ r ] [ i ] [ j ] + g [ r + 1 ] [ R ] + A + B ∗ ( j − i ) 2 → g [ l ] [ R ]

时间复杂度 O(n5) O ( n 5 )

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=55,INF=0x3f3f3f3f;
int n,mx,A,B,a[N],b[N],f[N][N][N][N],g[N][N];
void checkmin(int &x,int y){x=x<y?x:y;}
int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint(),A=getint(),B=getint();
    for(int i=1;i<=n;i++)a[i]=b[i]=getint();
    sort(b+1,b+n+1),mx=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+mx+1,a[i])-b;
    memset(f,INF,sizeof(f)),memset(g,INF,sizeof(g));
    for(int i=1;i<=n;i++)f[i][i][a[i]][a[i]]=0,g[i][i]=A,g[i][i-1]=0;
    g[n+1][n]=0;
    for(int l=n;l;l--)for(int r=l;r<=n;r++)
        for(int i=1;i<=mx;i++)for(int j=i;j<=mx;j++)if(f[l][r][i][j]!=INF)
        {
            for(int nr=r+1;nr<=n;nr++)
                checkmin(f[l][nr][min(i,a[nr])][max(j,a[nr])],f[l][r][i][j]+g[r+1][nr-1]);
            for(int nr=r;nr<=n;nr++)
                g[l][nr]=min(g[l][nr],f[l][r][i][j]+g[r+1][nr]+A+B*(b[j]-b[i])*(b[j]-b[i]));
        }
    cout<<g[1][n]<<'\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值