目录
1.模拟
1.1.什么是模拟
在自然界中,许多现象具有不确定的性质,有些问题甚至很难建立数学模型,或者很难用计算机建立递推、递归、枚举、回溯法等算法。在这种情况下,一般采用模拟策略。而模拟策略也就是模拟某个过程,通过改变数学模型的各种参数,进而观察变更这些参数所引起过程状态的变化,由此展开算法设计。通俗来说就是按照题目的要求,一步步写出代码。
1.2.特点
模拟题目通常具有码量大、操作多、思路较为繁琐的特点。并且由于它码量大,会导致很难查错。
1.3.模拟的过程
解决算法竞赛中的模拟题时,解题过程通常包括以下步骤:
-
理解问题:仔细阅读题目描述,确保对问题要求和限制条件有清晰的理解。理解问题的关键是确定输入和输出的格式、数据范围以及需要解决的具体任务。
-
设计算法:根据问题的要求,设计一个合适的算法来解决问题。这可能涉及到选择合适的数据结构、确定算法的时间复杂度和空间复杂度,并考虑如何处理边界情况。
-
编写代码:将算法转化为具体的编程代码。在编写代码时,要注意命名规范、代码的可读性和代码的健壮性。
-
调试和测试:对编写的代码进行调试和测试,确保它能够正确地解决问题。可以使用一些样例输入进行测试,验证代码的正确性。
-
分析复杂度:分析算法的时间复杂度和空间复杂度,确保算法在给定的数据范围内能够在合理的时间内运行。
-
性能优化:如果代码在给定的时间限制内无法完成任务,可以考虑优化算法。这可能涉及到改进算法的时间复杂度、减少不必要的计算或者使用更高效的数据结构。
-
提交解答:将最终的代码提交给评测系统,确保代码通过所有的测试用例。
-
总结经验:回顾解题过程,总结经验教训。思考如何更好地优化算法、提高解题速度和准确性。
这些步骤在解决算法竞赛中的模拟题时通常是适用的,但具体的解题过程可能因问题的复杂性和特殊性而有所不同。对于不同的题目类型,可能需要运用不同的算法和技巧来解决。因此,熟悉各种常见的算法和数据结构,并进行实践和练习,可以帮助提高解题能力。
2.思维
思维这个东西类似脑经急转弯,在算法竞赛中,一般简单的签到题就是一个小的思维题,往上难度的题目就是思维+算法,思维说起来比较抽象,提升思维的路线只有见得多,想得多,写得多。
2.1.例子
问题:
给定一个n个长度的序列a,你可以按任意顺序对其进行排列,排列完成后,每两个相邻元素的差的绝对值即为其快乐值,求按照某种排列后所有快乐值的总和最小是多少?
分析:
假如现在只有三个数 1,4,7,如何排列使得快乐值总和最小?
考虑将最小值与最大值放在两边 快乐值为6,再考虑4的位置4 1 7 1 7 4 7 1 4
以上三种情况你会发现只有1 4 7这样按照大小顺序排列的快乐值总和不会在6的基础上增加,因为其总和由 6 = 7-1 变成了 6 = (7-4)+(4-1) = 7-1,同理n个数更多时也是如此,所以当我们按照从小到大的顺序排列后ans = (a[2]-a[1])+(a[3]-a[2])+...+(a[n]- a[n-1]) = a[n]-a[1],所以答案即为max{a}-min{a};
3.构造
构造题是比赛中常见的一类题型。从形式上来看,问题的答案往往具有某种规律性,使得在问题规模迅速增大的时候,仍然有机会比较容易地得到答案。这要求解题时要思考问题规模增长对答案的影响,这种影响是否可以推广。例如,在设计动态规划方法的时候,要考虑从一个状态到后继状态的转移会造成什么影响。
3.1.特点
-
需要创造性思维:构造题要求参赛者具备创造性思维,能够设计和构造新的数据结构、算法或解决方案。参赛者需要思考如何构造满足问题要求的特定情况或特定性质的数据。
-
考察设计和构造能力:构造题注重参赛者的设计和构造能力。参赛者需要根据问题的要求,设计出满足条件的数据结构或算法,并能够有效地构造出符合要求的解决方案。
-
可能具有较高的难度:构造题通常是算法竞赛中的较难题目之一。它可能需要参赛者具备深入的数学知识、算法和数据结构的理解,以及解决复杂问题的能力。构造题可能需要参赛者花费较长的时间和精力来设计和构造满足条件的解决方案。
-
可能存在多种解决方案:构造题通常具有多样性,可能存在多种满足条件的解决方案。参赛者可以根据自己的设计思路和创造力,选择不同的构造方式来解决问题。
-
考察代码实现能力:构造题不仅要求参赛者有良好的设计能力,还需要能够将设计转化为具体的代码实现。参赛者需要编写出能够正确构造满足条件的数据结构或算法的代码。
构造题一个很显著的特点就是高自由度,也就是说一道题的构造方式可能有很多种,但是会有一种较为简单的构造方式满足题意。看起来是放宽了要求,让题目变的简单了,但很多时候,正是这种高自由度导致题目没有明确思路而无从下手。构造题另一个特点就是形式灵活,变化多样。并不存在一个通用解法或套路可以解决所有构造题,甚至很难找出解题思路的共性。
3.2.例题
问题:
https://codeforces.com/gym/102992/problem/K
分析:
该题有以下两个重要性质:
1.如果x与y相同,则gcd(x,y) = x
2.如果|x-y|=1,则gcd(x,y) = 1
所以至少有一组gcd(1,x)=1,所以k=0,输出-1;
解法:那么我们先将序列按原来的顺序排列,即:1,2,3...n,此时gcd(pi,i) = 1的数量只有1,如果k<n,那么我们只需要任意选取一段从1开始长度为k的序列,该序列左移即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n, t[100000];
void solve() {
int n, k;
cin >> n >> k;
if (k == 0) {
cout << -1;
return;
}
int count = k - 1;
for (int i = 1; i <= n; i++) {
if (count > 0) {
cout << i + 1 << ' ';
}
else if (count == 0) {
cout << 1 << ' ';
}
else {
cout << i << ' ';
}
count--;
}
}
int main() {
ios::sync_with_stdio;
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
4.博弈
在算法竞赛中,博弈题是一类涉及博弈理论和策略的问题。它模拟了两个或多个参与者之间的决策过程,需要设计出最优的策略来获得最大的利益或达到特定的目标。
4.1.例子
问题:
有n 颗石子,甲先取,乙后取,每次可以拿 1~m 颗石子,轮流拿下去,拿到最后一颗的人获胜。
分析:
(1)n<m 时,由于一次最少拿一个,最多拿 m个,甲可以一次拿完,先手赢。
(2)n=m+1时,无论甲拿走多少个(1~m 个),剩下的都多于1个且少于或等于m个,乙都能一次拿走剩余的石子, 后手取胜。
上面两种情况可以扩展为以下两种情况:
(1)如果n%(m+1)=0,即n是m+1的整数倍,那么不管甲拿多少个,如甲拿走k个,乙都拿m+1-k个,使剩下的永远是 m+1的整数倍,直到最后的m+1个,所以后拿的乙一定赢。
(2)如果n%(m十1)!=0,即n不是m十1的整数倍,还有余数r,那么甲拿走r个,剩下的是m+1的倍数,这样就转移到了情况(1),相当于甲乙互换,结果是甲赢。
在这个拿石子的游戏中,对于后拿的乙来说是很不利的,只有在n%(m+1)=0的情况下乙才能赢,其他所有情况都是甲赢。