THUSC2016 成绩单 和 LOJ3226 Greedy Pie Eaters
成绩单
期末考试结束了,班主任 L 老师要将成绩单分发到每位同学手中。L老师共有 n 份成绩单,按照编号从 1 到 n 的顺序叠放在桌子上,其中编号为 i 的成绩单分数为 Wi。
成绩单是按照批次发放的。发放成绩单时,L 老师会从当前的一叠成绩单中抽取连续的一段,让这些同学来领取自己的成绩单。当这批同学领取完毕后,L 老师再从剩余的成绩单中抽取连续的一段,供下一批同学领取。经过若干批次的领取后,成绩单将被全部发放到同学手中。
然而,分发成绩单是一件令人头痛的事情,一方面要照顾同学们的心理情绪,不能让分数相差太远的同学在同一批领取成绩单;另一方面要考虑时间成本,尽量减少领取成绩单的批次数。对于一个分发成绩单的方案,我们定义其代价为:
其中 k 是分发的批次数,对于第 i 批分发的成绩单,maxi 是最高分数,mini 是最低分数,a 和 b是给定的评估参数。
现在,请你帮助 L 老师找到代价最小的分发成绩单的方案,并将这个最小的代价告诉 L 老师。当然,分发成绩单的批次数 k 是由你决定的。
n≤50,a≤1500,b≤10,wi≤1000
题解
https://blog.csdn.net/qq_39677783/article/details/86898654
考虑到这题的抽取方式:每次从中间抽取一段,然后两边的又会拼起来,所以区别于序列划分类问题,前缀DP的状态不够清晰无法转移,可以想到区间DP。
先设g(i,j)表示将[i,j]这段区间的数全部消掉的最小代价,那么最终答案就是g(1,n)。但是这样会有问题:我们需要知道每次消去的maxi和mini是多少,才能方便转移。那么我们可以这样做:首先对wi离散化,假设tmpi表示原数组w中第i小的数;对于一个g(i,j),我们可以枚举值域区间[l,r],表示将[i,j]这段区间里的数全部消掉之前最后一次消去的数都在值域范围[l,r]当中。那么最后一次消去显然会产生a+b×(tmpr−tmpl)2的代价。那么接下来的问题就是:将区间[i,j]中的数消到只剩下值域范围在[l,r]中的数,最小代价是多少,不妨设其为f(i,j,l,r)。
这样一来我们g(i,j)的转移方程就有了:
下面考虑f(i,j,l,r)的转移。一个显然的思路是
然而很不幸这样做会漏掉一些情况。
正确的做法是:首先我们可以找到[i,j]区间里左边第一个不在值域范围[l,r]中的数的位置p,以及右边第一个不在值域范围[l,r]中的数的位置q,如果存在这样的区间[p,q](如果不存在当然就不用管了),那么
好了,这两个转移方程出来之后,剩下的就是区间DP套路了。当然是枚举区间长度再枚举左端点做DP就行了。
时间复杂度O(n5)
CO int N=60,inf=1e9; int w[N],tmp[N]; int f[N][N][N][N],g[N][N]; int main(){ int n=read<int>(),a=read<int>(),b=read<int>(); for(int i=1;i<=n;++i) read(w[i]); copy(w+1,w+n+1,tmp+1); sort(tmp+1,tmp+n+1); int all=unique(tmp+1,tmp+n+1)-tmp-1; for(int i=1;i<=n;++i) w[i]=lower_bound(tmp+1,tmp+all+1,w[i])-tmp; for(int i=1;i<=n;++i){ g[i][i]=a; for(int l=1;l<=all;++l)for(int r=l;r<=all;++r) if(w[i]<l or w[i]>r) f[i][i][l][r]=a; } for(int len=2;len<=n;++len)for(int i=1,j=i+len-1;j<=n;++i,++j){ g[i][j]=inf; for(int l=1;l<=all;++l)for(int r=l;r<=all;++r){ int p=0,q=0; for(int t=i;t<=j;++t) if(w[t]<l or w[t]>r) {p=t; break;} for(int t=j;t>=i;--t) if(w[t]<l or w[t]>r) {q=t; break;} if(p and q) f[i][j][l][r]=g[p][q]; else f[i][j][l][r]=inf; for(int k=i;k<j;++k) f[i][j][l][r]=min(f[i][j][l][r],f[i][k][l][r]+f[k+1][j][l][r]); g[i][j]=min(g[i][j],f[i][j][l][r]+a+b*(tmp[r]-tmp[l])*(tmp[r]-tmp[l])); } } printf("%d\n",g[1][n]); return 0; }
Greedy Pie Eaters
Farmer John 有 M 头奶牛,为了方便,编号为 1…M。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。Farmer John 一天烤了 N 个派请奶牛吃,这 N 个派编号为 1…N。第 i 头奶牛喜欢吃编号在 [li,ri] 中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第 i 头奶牛有一个体重 wi,这是一个在 [1,106] 中的正整数。
Farmer John 可以选择一个奶牛序列 c1,c2,…cK,并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛 ci 吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 [lci,rci] 中所有剩余的派。Farmer John 想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按 c1,c2,…cK 的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重(wc1+wc2+…+wcK)是多少。
对于全部数据,1≤N≤300,1≤M≤N(N−1)2,1≤li≤ri≤N,1≤wi≤106。
题解
http://jklover.hs-blog.cf/2020/06/07/Loj-3226-Greedy-Pie-Eaters/#more
首先可以假定所有\binom{n}{2}个区间都对应了一头牛,没有给出的可以将其牛的重量看作 0 .
当某头牛要吃区间 [l,r] 时,若剩下的派大于 1 ,可以先让其他牛吃到还剩一个,于是每头牛都恰好吃掉一个派.
设 f(l,r) 表示把 l,r 内的派吃完能获得的最大收益,转移时枚举最后被吃的派是 k ,
其中 g(l,r,k)=\max_{l\le x\le k\le y \le r} A_{x,y} ,表示用区间不超出 [l,r] 的牛吃掉第 k 个派能获得的最大收益.
转移时考虑 [l,r] 这个区间的贡献就行了.
时间复杂度 O(n^3) .
CO int N=310; int a[N][N],g[N][N][N],f[N][N]; int main(){ int n=read<int>(); for(int m=read<int>();m--;){ int w=read<int>(),l=read<int>(),r=read<int>(); a[l][r]=w; } for(int len=1;len<=n;++len) for(int l=1,r=l+len-1;r<=n;++l,++r) for(int k=l;k<=r;++k){ g[l][r][k]=max(a[l][r],max(g[l+1][r][k],g[l][r-1][k])); f[l][r]=max(f[l][r],f[l][k-1]+f[k+1][r]+g[l][r][k]); } printf("%d\n",f[1][n]); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· SQL Server统计信息更新会被阻塞或引起会话阻塞吗?
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 本地部署 DeepSeek:小白也能轻松搞定!
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)