P5336 [THUSC2016]成绩单

题意:

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

分析:

这个题属实有些抽象,卡了好久

显然,这个题涉及到区间合并,与之类似的还有P2135 方块消除 这道题。一般涉及到区间合并,就要求对该区间以后的区间记录一定的信息,一般在状态设计上处理。这样的模型称为挂载模型

那么,对于这道题来说,除了区间的左右端点,影响该区间的代价还有区间内的最大最小值。将四个要素塞入状态中,定义状态 dp[l][r][mn][mx] 表示把区间 [l,r] 和接在这个区间后面的,满足这个区间里的最大值为 mx,最小值为 mn 的另一个区间内的成绩单全部发放完毕,所需要的最小代价。也就是说,这个状态定义的是前后两个区间拼起来的总区间。

这样的状态设计就有点绕,想起来就更抽象了。不过它却是符合挂载模型的思维方法的,令人感慨。另一个奇怪的点是,如果你要表示分发位置为 i 的成绩单所需要的代价,其状态就是 dp[i][i1][w[i]][w[i]]。区间的左端点虽然大于了右端点,但是无伤大雅,说明前面一个区间是不存在的,而后面那个区间的最小值等于最大值,都为 w[i],于是就表示出了分发位置为 i 的成绩单所需要的代价。

这个题的预处理也比较奇怪,不过比较类似上一段的叙述。
根据上面的想法,用 dp[i][i1][mn][mx] 就可以表示 i 位置以后的一个区间最大值为 mx,区间最小值为 mn 的一个区间。因此预处理如下:

dp[i][i1][mn][mx]=a+b(mxmn)(mnmx)

接下来说说怎么转移。
对于区间 [l,r],状态 dp[l][r][mn][mx] 来说,你可以选择直接把区间 [l,r] 作为一批来发放,再把后面一个满足区间最大值为 mx,区间最小值为 mn 的区间作为另一个批次,两个批次分别发放;也可以将这个区间和后面一个满足区间最大值为 mx,区间最小值为 mn 的区间并起来作为一个批次一起发放;还可以在区间 [l,r] 中枚举一个 k,把区间 [k+1,r] 单独作一个批次分发,把区间 [l,k] 与后面一个满足区间最大值为 mx,区间最小值为 mn 的区间并起来作为一个批次分发。
上面三种选择分别对应了如下方程:

dp[l][r1][w[r]][w[r]]+a+b(mnmx)(mnmx)

dp[l][r1][mx][mn]

dp[l][k][mx][mn]+dp[k+1][r1][w[r]][w[r]]l<=k<r

dp[l][r][mn][mx] 在上面三种转移中取一个 min 即可。

#include<bits/stdc++.h> using namespace std; const int MAXN = 50; int dp[MAXN + 5][MAXN + 5][MAXN + 5][MAXN + 5],n,A,B,w[MAXN + 5]; vector<int> lsh; int main(){ memset(dp,0x3f,sizeof dp); scanf("%d%d%d",&n,&A,&B); for(int i = 1; i <= n; i++){ scanf("%d",&w[i]); lsh.push_back(w[i]); } sort(lsh.begin(),lsh.end()); lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end()); for(int i = 1; i <= n; i++){ w[i] = lower_bound(lsh.begin(),lsh.end(),w[i]) - lsh.begin() + 1; } for(int i = 1; i <= n; i++){ for(int x = 1; x <= lsh.size(); x++){ for(int y = x; y <= lsh.size(); y++){ dp[i][i - 1][x][y] = A + B * (lsh[x - 1] - lsh[y - 1]) * (lsh[x - 1] - lsh[y - 1]); } } } for(int len = 1; len <= n; len++){ for(int l = 1; l + len - 1 <= n; l++){ int r = l + len - 1; for(int mi = 1; mi <= lsh.size(); mi++){ for(int mx = mi; mx <= lsh.size(); mx++){ dp[l][r][mi][mx] = min(dp[l][r][mi][mx],dp[l][r - 1][w[r]][w[r]] + A + B * (lsh[mx - 1] - lsh[mi - 1]) * (lsh[mx - 1] - lsh[mi - 1])); dp[l][r][mi][mx] = min(dp[l][r][mi][mx],dp[l][r - 1][min(mi,w[r])][max(mx,w[r])]); for(int k = l; k < r; k++){ dp[l][r][mi][mx] = min(dp[l][r][mi][mx],dp[l][k][mi][mx] + dp[k + 1][r - 1][w[r]][w[r]]); } } } } } cout << dp[1][n - 1][w[n]][w[n]] << "\n"; }

__EOF__

本文作者Never Gonna Give You Up!
本文链接https://www.cnblogs.com/CZ-9/p/17367064.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   腾云今天首飞了吗  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示