题解:UVA12034 比赛名次 Race && 一般 dp 题的做题技巧
dp。
本篇题解将从 dp 的状态设计、转移设计、初值设计、答案统计和优化五方面进行说明,旨在讲明白这道题和复习 dp。
注意这道题的 dp 是一些个人理解的积累,如果与真正的定义不同还请见谅。
Solution UVA12034
状态设计
设
- 为什么这么设计?
设计一个 dp 的状态要从题目要求和转移方便性考虑。
如这道题,题目要求的是方案数,我们就设方案数。再如 P10954 LCIS 这道题,我们的状态设计就是与转移方便性有关。
一个好的 dp 状态可以做到方便地转移和统计答案。dp 状态的不同直接决定了程序时间复杂度的不同。
- 如何去设计?
正常情况下,题目要求什么我们就设什么。但是具体是几维 dp,这个就跟具体题目有关。我们可以观察数据范围,得到一个初步的方向。
转移方程
这道题的转移方程显然是
我们剖析一下这个状态转移方程。
前半部分指的是这个新加进来的人和前面一个人并列,显然就在一个名次上,方案有
后半部分指的是这个新加进来的人不和前面任何一个人并列,
一个好的 dp 状态设计可以使得 dp 转移方程浅显易懂,也可以降低 dp 转移的复杂度。
在我们设计好状态之后,我们往往要从实际问题出发,去考虑这一个状态可以由哪些状态转移而来。
初值设计
这题的初值就是
初值的设计十分重要。如果 dp 失去了初值的设计,就相当于一堆
dp 的初值设计一般需要手推和感性理解。例如这题,
答案统计
本题答案:
答案统计就很简单了,你只需要看合法的状态有哪些就行。
优化
dp 的优化分时间上的优化和空间上的优化两种。
dp 复杂度
要学会 dp 优化,就要先学会 dp 复杂度的计算。
dp 复杂度分状态复杂度和转移复杂度两部分。
很显然:先要枚举这个状态,然后用一部分复杂度转移。
时间上的优化
时间上的优化主要有前缀和优化、线段树或树状数组优化、单调队列优化等等。
这些优化可以使 dp 转移的复杂度降一维或者变为一只
有些时候 dp 的优化是可以互通的,如 P1725 琪露诺,既可以单调队列优化 dp,也可以线段树优化 dp。
空间上的优化
空间一般就是滚动数组,这种在背包 dp 中也很常见。
本题也可以使用滚动数组优化,只不过这题空间明显不卡,所以没有大用处。
有时背包 dp,如果开二维数组 int
显然会 MLE,但是滚动数组滚掉一维就不会了。
注意:滚动数组并不能减少状态复杂度。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1005,mod=10056;
int dp[N][N],sum[N];
void init(int n,int m){
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=(dp[i-1][j]*j+dp[i-1][j-1]*j)%mod;
sum[i]=(sum[i]+dp[i][j])%mod;
}
}
}
void solve(int id){
int n;
scanf("%d",&n);
printf("Case %d: %d\n",id,sum[n]);
}
int main(){
init(1000,1000);
int T;
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
return 0;
}
祝愿大家在 CSP 中取得理想的成绩。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效