数据结构与算法课程设计——CSP202109

CSP202109

00 一些补充知识

  • 常用stl内容

    • sort函数的使用是必须掌握的。
    • vector, map, set的使用。
  • 常用算法与思想

    • 二分

      二分的核心思想是:对于某个性质 C C C,以及有限良序集 S S S ( ∃ s ∈ S ) ( ∀ s ′ ≤ s ) ( ∀ s ′ ′ > s ) ( C ( s ′ ) ≠ C ( s ′ ′ ) ) (\exist s\in S)(\forall s'\le s)(\forall s'' >s)(C(s')\neq C(s'')) (sS)(ss)(s′′>s)(C(s)=C(s′′))

      直观来说,在一个良序集,存在一个分界线,两侧的元素有某个不同的性质。

      • 二分查找:可以使用函数 l o w e r _ b o u n d ( b e g i n , e n d , x ) lower\_bound(begin, end, x) lower_bound(begin,end,x)找到范围内第一个不小于x的元素,或使用 u p p e r _ b o u n d ( b e g i n , e n d , x ) upper\_bound(begin, end, x) upper_bound(begin,end,x)找到第一个大于x的元素
      • 对答案二分:通常有最大化最小值、最大化平均值、第k大值等
    • 前缀和/差分

      • 二者互为逆运算。用来求一段区间 [ l e f t , r i g h t ) [left,right) [left,right)的和。
    • 贪心

      • 一种思想:可以看看Dijkstra算法和决策树。并不一定可以得到最优解。
      • 贪心通常用来提出一种先取最大/小值猜想,之后要证明猜想能导出最优解,通常使用数学归纳法。
    • 动态规划

      • 通常有背包问题等,通过之前状态与状态转移方程,推出当前状态。
      • 个人认为和第二数学归纳法很像。
    • 复杂度分析

      • 时间复杂度分析:排序为nlogn,循环为循环长度。嵌套代码时间复杂度要相乘,否则取多块的最大值。

      如果代入n后,时间复杂度大于1e9,通常说明复杂度过高。

      • 空间复杂度分析:比较少,通常数组大小不要超过 4 × 1 0 6 4\times 10^6 4×106即可。
  • 简单的文件读写

    • 使用以下代码,可以改变io文件,但是记得提交时将其注释掉或删除
      int main() {
          //将in.txt和out.txt改为你的输入/输出文件
      	  freopen("in.txt", "r", stdin);
          freopen("out.txt", "w", stdout);
      }
    
    • 使用以下代码,可以加快cin读取速度,通常CSP认证的题目中不需要使用
    int main() {
    	cin.tie(0);
    	ios::sync_with_stdio(false);
    }
    

01 数组推导

题目

在这里插入图片描述

标签

​ 看不出来

输入输出

在这里插入图片描述

数据范围

在这里插入图片描述

思路

​ 显然,通过数组 A A A可以求得数组 B B B,但反过来只能求出数组 A A A每个元素的范围。

​ 注意到:对于集合 M ,   N M,~N M, N以及运算 + + +(∪),max运算具有吸收律。对于 B i B_i Bi,有
B i = m a x ( A 1 , … , A i ) = m a x ( m a x ( A 1 , … , A i − 1 ) , m a x ( A i ) ) = m a x ( B i − 1 , A i ) \begin{aligned} B_i&=max(A_1,\dots,A_i)\\ &=max(max(A_1,\dots,A_{i-1}), max(A_i))\\ &=max(B_{i-1},A_i) \end{aligned} Bi=max(A1,,Ai)=max(max(A1,,Ai1),max(Ai))=max(Bi1,Ai)
B i − 1 = B i B_{i-1}=B_i Bi1=Bi时, 0 ≤ A i ≤ B i − 1 0\leq A_i\leq B_{i-1} 0AiBi1;当 B i − 1 < B i B_{i-1}<B_i Bi1<Bi时, B i = A i B_i=A_i Bi=Ai,就可以求得 A A A中每个元素的可能范围

注意

  • A中元素都是自然数

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
	int n;
	cin >> n;
	vector<int> B(n+1);
	int maxima = 0, minima = 0;
	for (int i = 1; i <= n; ++i) {
		cin >> B[i];
		maxima += B[i];
		minima += (B[i] > B[i-1]) ? B[i] : 0;
	}
	cout << maxima << endl << minima;
	return 0;
}

02 非零段划分

题目

在这里插入图片描述

标签

​ 很有意思的思维题?

输入输出

在这里插入图片描述

数据范围

在这里插入图片描述

思路

  1. 因为要求非零段的个数,那在数组前后各添加一个0,不会影响结果。

  2. 因为每次将小于p的数字变为0,所以连续的相同数字可以看作一个数字

  3. 进行了上面两个变换,现在数组的情况如下( A i A_i Ai代表非零段):
    0   A 1   0   A 2   … A m   0 0~A_1~0~A_2~\dots A_m~0 0 A1 0 A2 Am 0
    ​ 考察非零段 A A A,由于其中不存在连续的相同元素,且前、后一定是零段。令 A = { 0 } + A + { 0 } A=\{0\}+A+\{0\} A={0}+A+{0} A A A中的每个元素 a i a_i ai一定满足下面几种情况之一:

    1. a i > a i − 1 且 a i > a i + 1 a_i>a_{i-1}且a_i>a_{i+1} ai>ai1ai>ai+1。这种情况下, a i a_i ai变为0时,其两侧元素已为0,非零段数量减少
    2. a i < a i − 1 且 a i < a i + 1 a_i<a_{i-1}且a_i<a_{i+1} ai<ai1ai<ai+1。这种情况下, a i a_i ai变为0时,两侧元素均不为0,非零段数量增加
    3. otherwise, a i a_i ai变为0时,非零段数量不变。
  4. 由于每个数字不大于 1 0 4 10^4 104,可以记录下每个数值的数字变为0时,对非零段数量的影响。对于数值 m m m,其影响 d i f [ m ] dif[m] dif[m]由上面三种情况确定,权重分别为 − 1 , + 1 , 0 -1,+1,0 1,+1,0。将小于 p p p的数变为0,其改变的非零段数量为 ∑ k = 1 p − 1 d i f [ k ] \sum_{k=1}^{p-1}dif[k] k=1p1dif[k]。选取所有 p p p中增加数量最大的,加上初始非零段数量 s s s即可。

注意

  • 注意看题?
  • 使用std::unique()函数应该也可以去重,我没有试过。此函数通常在离散化问题中使用,有兴趣可以看看。

代码

#include <bits/stdc++.h>
using namespace std;

int n, a;
const int N = 10007;
int main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	cin >> n;
	vector<int> A(1), dif(N);
	for (int i = 1; i <= n; ++i) {
		cin >> a;
		if (a == A[A.size()-1]) continue;
		A.push_back(a);
	}
	if (A[A.size()-1]) A.push_back(0);
	for (int i = 1; i < A.size() - 1; ++i) {
		if (A[i] < A[i-1] && A[i] < A[i+1]) {
			dif[A[i]]++;
		} else {
			if (A[i] > A[i-1] && A[i] > A[i+1]) {
				dif[A[i]]--;
			}
		}
	}
	int ans = 0, s = -1;
	for (int i = 0; i < A.size(); ++i) {
		s += (A[i] == 0);
	}
	for (int i = 1; i < N; ++i) {
		s += dif[i];
		ans = max(ans, s);
	}
	cout << ans;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值