1 条题解
-
0
自动搬运
来自洛谷,原作者为

MTFlowCzq
我是心流czq搬运于
2025-08-24 21:15:09,当前版本为作者最后更新于2023-07-12 21:15:35,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
通过数低?没有题解?那我来写一篇吧(
B 题库的题都这么难了?)题目大意
有 场比赛,你准备选不超过 场来打,你希望打完以后比赛分最高,初始为 。
你预知了每场比赛打完后的表现分,设打比赛之前的比赛分为 ,表现分为 ,则打完后比赛分会更新为 。除此之外,每场比赛有 div1 和 div2 两种类型,div2 的比赛只有在打比赛前的比赛分小于 时才能参与。
写程序求出最高比赛分。
解题思路
先说一句,尽管题目没有明确说,打比赛的顺序是不能调换的(常识好吧),并且别忘了限制了参加场数。
简单的贪心是不凑效的,因为样例已经给出了一组先减分再加分的数据。容易发现难点在于 div 类型的处理。
如果不考虑 div2 限分,那么使用动态规划即可解决问题,令 为前 场中打 场比赛的最高分,那么第 场比赛可以不打,也可以打了上分(容易发现表现分相同时,初始分越高,打完分数越高),转移方程为:
$$f_{i,j}=\max(f_{i-1,j},x+\lfloor\frac{a_i-x}{4}\rfloor),\ x=f_{i-1,j-1} $$而在加入类型之后,可以强制下分来打 div2,那么是否可以再维护小于 的最高分呢?思考后会发现难以转移(新的小于 的最高分可能由更小的分数转移得到),这样做似乎不行。一种想法是用 表示前 场比赛参加 场且分数为 的可行性,但这样三维的状态会超时,需要二维的。
经过思考以后,我们发现,可以反过来设计状态,将题目的限制和要求的得分颠倒(经典的 DP 优化手段),变为“要达到一定的分数,至少需要打多少比赛”。于是我们令 为前 场比赛中分数恰为 时最少的比赛场数。这样,我们就可以用 来更新 了,转移式为:
$$dp_{i,x}=\min(dp_{i,x},dp_{i-1,j}+1),\ x=j+\lfloor\frac{a_i-j}{4}\rfloor $$而对 div2 的比赛,只需加上 的限制条件即可。最后找出最大的满足 的 ,就是答案。
由于比赛分不会超过 ,状态数是 ,时间复杂度是 。由于 ,,可以通过本题。
代码
值得一提的是,空间可以使用滚动数组优化,但此时需要注意更新顺序(类似 0-1 背包,每场比赛只能打一次,所以先更新靠近 的部分)
#include<iostream> #include<algorithm> using namespace std; int n,k,x,dp[4009],id,a; int main() { cin>>n>>k>>x; for (int i=0;i<=4000;i++) dp[i]=10009; //初设为 INF,大于 n 即可 dp[x]=0; //一场比赛都不打 while (n--) { cin>>id>>a; int m=4000; if (id==2) m=1899; //参与比赛的比赛分限制 for (int i=a+1;i<=m;i++) { //此处注意两个循环的遍历顺序 int now=i-(i-a+3)/4; //计算打完后的分数 dp[now]=min(dp[now],dp[i]+1); //试图更新 } for (int i=min(m,a-1);i>=0;i--) { int now=i+(a-i)/4; dp[now]=min(dp[now],dp[i]+1); } } for (int i=4000;i>=0;i--) if (dp[i]<=k) { //找答案 cout<<i<<endl; break; } return 0; }
本人第一篇题解,如有问题和建议欢迎指出!
- 1
信息
- ID
- 8513
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者