noip2001提高组题解

本文深入探讨了四种经典算法竞赛题目:一元三次方程求解、数的划分方案数计算、统计单词个数最优化及复杂路线规划问题。通过详细解析枚举法、记忆化搜索、动态规划和图论在各题中的应用,分享了高效算法设计思路与实战经验。

T1.一元三次方程

给出一个形如a*x^3+b*x^2+c*x+d=0的一元三次方程的系数a,b,c,d,保证有三个不同的实根,输出并保留两位小数。

由于解的范围比较小,-100到100,果断用枚举,因为保留两位小数,所以可以放大【放大法??大概的吧】。。然后既可以直接从-10000到10000进行枚举,判断是否是解的时候,就缩小100倍,判断和零的差是否足够小即可。

当然这是因为数据范围。假设把解的范围放大到-10000到10000的话,放大法的效率就不显得那么高。所以可以采用二分。由于两根之差的绝对值>=1,所以可以枚举长度为1的区间,然后对于这个区间中的数进行二分求解。(显然,判断根所在的区间就和数学里判断零点所在是一模一样的)

 

T2.数的划分

给出n,要把它分成k个数的和。求方案数。

一开始因为数据小,只有6层,就打了个暴力dfs。。。当然也是能过的,只是200,6的数据跑了0.7s…

然后因为打完后时间还多,就开始想优化。。觉得这个状态可以变一下,直接这样是优化不到哪里去的。。

然后就改了,用f(remain,maxn,cnt)作为状态,表示剩余remain的数要分成cnt个数的和,且剩下的数最大不超过maxn。。。这样的话,就可以用一下记忆化搜索。还有一个递推方程。。。

f(remain,maxn,cnt)=f(remain-maxn,maxn,cnt-1)+f(remain,maxn-1,cnt)

这样的话,效率就高很多了。。。

然后老师说这个状态其实还是可以优化的。。。

然后也确实是这样。。。我们可以考虑一个状态f[i][k]表示将大小为i的数分成k份的方案数,然后可以发现,划分的最小数为1时是f[i-1][k-1],然后不为1时,可以视为f[i-k][k]的所有数+1构成的方案数,所以f[i][k]=f[i-1][k-1]+(i>k)*f[i-k][k]就是dp方程。这样就简单的多了。

 

T3.统计单词个数

给出p行(每行20个)字母组成的字符串(一整个),要划分成k段,使得每段包含的单词数之和最大。。(单词种类不超过6个,输入给出)

一开始想想也是比较像dp的,所以只要解决每段里的单词数应该就没问题了。

所以问题就是单词了吧。。。由于题目中说,每个字母只能起头一个单词,这个倒是个突破口。。。由于我们要尽量提高空间利用率,就可以把每位字母开头的单词的最小长度used[]计算出来。(这个嘛,不就是枚举吗。。。)然后可以枚举区间i,j,计算区间内的单词个数num[i][j].只要用一个动点p枚举区间中的每个字母,看看p+used[p]-1是否在区间范围内,确定是否统计就好了。

然后可以借助这些预处理来dp。。用f[i][k]表示将[1,i]这个区间分成k段的最大单词总数,显然有是枚举断点p,取f[i][k]=max{f[p][k-1]+num[p+1][i]}就好了。

 

 

T4.Car的旅行路线

(个人认为处理图的时候非常烦)

给出s个城市,每个城市有四个机场,呈矩形顶点分布。每个城市内部有高铁路线连接每两个机场,单位里程运费Ti。每个城市的机场有航线通往每个不同城市的每个机场,单位运费是T。现在Car要从城市A到城市B,求最小费用。

由于题目输出比较懵,只给了三个顶点,所以还要自己算第四个。。。还要先判断给出的直角在哪里。。。【于是恶狠狠地打了个向量数量积为零(滑稽.jpg)】

然后我是先处理了每个城市内部的交通费用(顶点从1到4*n编号,每四个为一座城市),然后处理城市间的交通费用。(这是个完全图啊我去。。。)

然后。。。然后枚举A城的四个机场,做了四次SPFA,每次从B城的四个机场里取一个最小距离。。。然后。。就没了。。。

写的时候脑残了那么一下下,预处理距离的时候,存的是邻接矩阵,然后。。。只存了半边。。忘记还要两个下标反一下了。。。就WA了一个点。(无奈.jpg)

转载于:https://www.cnblogs.com/zcyhhh/p/6685930.html

### NOIP 2017 提高 初赛题目解析 #### Pascal 编程语言的淘汰时间 关于 Pascal 编程语言何时不再被支持的问题,在选项中给出的时间点为 2022 年[^1]。 #### 归并算法最坏情况下的比较次数 对于两个长度均为 n 的有序数 A 和 B 进行归并操作时,如果仅考虑元素之间的比较作为基本运算,则在最坏的情况下,至少需要执行 \(2n - 1\) 次比较才能完成整个过程[^2]。 ```python def merge_sorted_arrays(A, B): merged_array = [] i, j = 0, 0 while i < len(A) and j < len(B): if A[i] <= B[j]: merged_array.append(A[i]) i += 1 else: merged_array.append(B[j]) j += 1 # Append any remaining elements from either list merged_array.extend(A[i:]) merged_array.extend(B[j:]) return merged_array ``` 此代码展示了如何有效地将两个已排序列表合并成一个新的已排序列表,并且在这个过程中进行了必要的比较来决定哪个元素应该先加入到新的列表当中去。当处理到最后一个元素的时候,实际上已经完成了 \(2n-1\) 次比较(假设两部分都含有 n 个不同大小的数),这正好对应于上述提到的最佳下界理论值。 #### 硬币分堆与查找不合格品的方法 给定一数量为 n 枚相同外观但可能有一枚质量不同的硬币成的集合 A ,通过天平称重的方式找出那枚与众不同的假币: 1. 计算 k 值等于向下取整后的 \(\lfloor{n / 3}\rfloor\); 2. 把这些硬币分为三份 X、Y 和 Z,每一份分别有 k 枚; 3. 如果 X 和 Y 的质量不相等的话,那么继续下一步骤;否则跳转至第 5 步; 4. 对较轻的那一侧进一步分割重复以上步骤直到找到唯一可疑对象为止; 5. 当剩下不超过两枚硬币时直接对比即可判断哪一个是异常者;而只有一枚的情况则默认其就是那个特殊个体[^3]. 这种策略利用了三分法的思想,每次都将待测样本缩小三分之一左右的比例,从而提高了效率同时也简化了解决方案的设计难度.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值