草庐IT

noip基础算法

今人不见古时月,今月曾经照古人 2023-03-28 原文

枚举

对于一些简单的题目,我们或许不需要用什么太巧妙的方法,只需要把所有的可能性列举出来,然后逐一试验就可以了。

总的来说,枚举就是通过列举所有的可能性进行一一判断检查。

方法

通过事先把各种可能发生的事情都列举一遍,为后面求解提供结果。

总的来说,两种方法:

递归枚举,这种方法往往更直观;

递推(循环)枚举,这种方法往往写起来更简洁。

常见类型

枚举排列

枚举子集

优化

在某些情况下,直接枚举可能会带来较差的效果,在这种时候,我们实际上可以分析题目,根据题目的一些性质或特点去除大部分的列举,减小枚举量,从而提高枚举的效率。

搜索

搜索,某种意义上就是对于枚举算法的一种改进,通过在枚举的过程中,不断排除一些不可能达到的情况,从而达到优化复杂度的效果。

深度优先搜索(DFS)

深度优先搜索的思想就是:从一个顶点沿一条路一直走,如果发现到不了目标节点,则返回上一个节点,沿另一条路一直走到底。

总的来说,DFS就是一个一个处理到底,发现无法得到结果之后,逐步返回寻求其它的出路。

DFS的应用并不局限于一般的图上,其往往用于一些状态图上。

剪枝

指通过过程当中的一些量确定不可行或不符合最优的情况。

宽度优先搜索(BFS)

宽度优先搜索的核心思想在于:首先访问起始节点的所有邻接点,然后再访问较远的区域,逐步扩大范围,直到找到目标节点。

BFS也主要运用于处理状态图,尤其在一些寻求最优方案的题目中有较大的用处。

BFS处理不含边权的图的求解最短路径问题非常有效。

两种方法的优缺点

深度优先搜索,优点在于能较高效地逼近解,缺点在于初始递归方向错误会带来很严重后果;

宽度优先搜索,优点在于能迅速找到答案不算大的解,缺点在于解答案较大时所耗时间与空间都比较大。

迭代加深搜索

在综合以上两种算法之后,出现了一个折中的方法:

通过限定下界k,然后先深度优先搜索k层,如果仍没有找到有效解,则增大下界。

这个方法的优点在于:相对于深度优先搜索并没有大很多的时间开销,但能部分解决深度优先搜索的局限,没有宽度优先搜索一般的大空间需求。

分治

分治算法的基本思想:将一个较大规模的问题分成多个(一般是2个)较小规模的互相独立且与原问题相同的子问题,那么只需要通过对子问题的求解,就可以得到原问题的解。

往往子问题会采取相同的方法继续分治下去,因此分治算法一般会采取递归的方式表现。

步骤

分解:将要解决的问题划分成若干规模较小的同类问题;

求解:当子问题划分得足够小时,用较简单的方法解决;

合并:按原问题的要求,将子问题的解逐层合并构成原问题的解。

应用

二分查找

归并排序

快速幂

贪心

贪心算法指的是在对问题求解过程中,总是做出目前来看最优的选择,也就是说贪心算法不会考虑全局最优解,只会不断考虑局部最优解。

但是,我们需要注意到,局部最优解不一定就是全局最优解。

贪心算法往往可以以较低的代码复杂度与时间复杂度而得到较优的结果,对于求解近似值的作用很大。

而对于相当一部分需要求解最优值的问题,我们会发现通常可以采用动态规划或者网络流的方法取代贪心算法。

考试技巧

STL

STL 是“Standard Template Library”的缩写,中文译为“标准模板库”。

#include<algorithm>

#include<bits/stdc++.h>(推荐)

sort()

sort函数用于给一个数组进行排序,在algorithm库里。

使用方法为sort(v.begin(),v.end(),cmp)),这里的v.begin()是开始的指针位置,v.end()是结束的指针位置(这里的表示是左闭右开,也就是说v.end()并不在排序内容里),cmp可要可不要,如果使用的是自定义类型且没有重载运算符就一定需要。

这个数组的类型可以是自定义类型或者STL中的vector,需要注意的是如果采用的是自定义类型则需要重载运算符(也可以如上面说的一样用cmp)。

stack

模拟栈,在<stack>里。

stack <Type> A;

常用函数

A.push(a);

入栈

A.pop();

出栈

A.top();

返回栈顶元素(但不出栈)

queue

模拟队列,在<queue>里。

queue <Type> A;

常用函数

A.push(a);

入队

A.pop();

出队

A.front();

返回队首元素(但不出队)

deque

双端队列

priority queue

优先队列,一个类似于堆的数据结构,在<queue>里。

默认是大根堆,如果想让他是小根堆的话有两种办法,其中一种是重载小于号。

能以O(logN)复杂度完成插入元素,删除最值,寻找最值。

Priority_queue <Type> A;

常用函数

A.push(a);

插入

A.pop();

删除最值(默认为最大值)

A.top();

返回最值(但不删除)

pair

一个包含两个可以不同的数据值的类型。

pair <Type1,Type2> A;

往往Type1,Type2都是标准类型,但如果是自定义类型,需要重定义运算符;

常用函数

赋值:make_pair(t1,t2);

返回对应的值:A.first,A.second;

vector

向量(Vector)是一个封装了动态大小数组的顺序容器。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的不定长的动态数组,在vector库里。

定义方式:vector <Type> A;

容器特性

顺序序列

顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。

动态数组

支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。提供了在序列末尾相对快速地添加/删除元素的操作。

能够感知内存分配器的(Allocator-aware)

容器使用一个内存分配器对象来动态地处理它的存储需求。

相当于是个动态数组,每次可以往末端插入一个元素,下标从0开始。

实现方式是每次不够大的时候暴力倍长,可以发现均摊是线性的。

常用操作

A.clear();

清空

A.push_back(a);

尾部添加元素

A.pop_back();

尾部删除元素

A.empty();

检查是否为空,空返回true

v.size()

这个一个unsigned int类型。也就是说对空的vector的size()-1会得到2^32-1。因此写代码的时候应带尽量避免这种写法。(或者强制类型转化成int)

v.resize()

其复杂度是O(max(1, resize()中的参数-原来的size()))的。

如果是大小变大的resize(),且可以指定新扩展的位置的值。若未指定则调用其默认构造函数,例如int之类的会默认是0。

v.clear()和vector<int>().swap(v)的区别。

前者是假装清空了,实际内存没有被回收。

后者是真的回收了,不过需要和v.size()的大小成正比的时间。

后者的意思是使用vector<>()这句话调用无参的构造函数生成一个vector<>类型的对象,然后和v交换,之后其生存期结束被销毁会自动调用其~vector<>()析构函数。注意<>里面要写v一样的类型(例如int)

set

一个存储集合的容器,在set库里。

map相当于是一个下标可以是任何数值的数组,如果访问时没有赋值就会返回零。

内部使用红黑树(一种平衡树)实现。

当set<>中的第一个参数是自定义类型的时候需要重新定义小于号。

复杂度基本上是O(log(当前大小))。

定义方式:set <Type> A;

常用函数

A.insert(a);

插入a

A.erase(a);

删除a:

A.find(a);

查找a,如果查找成功,返回对应指针,查找失败返回尾指针

A.begin(),A.end();

返回头指针与尾指针,尾指针不存储具体内容

map

存储一个从key到value的映射。某种意义上就是“广义”数组,在map库里。

map相当于是一个下标可以是任何数值的数组,如果访问时没有赋值就会返回零。

内部使用红黑树(一种平衡树)实现。

当map<,>中的第一个参数是自定义类型的时候需要重新定义小于号。

复杂度基本上是O(log(当前大小))

map <Type1,Type2> A;Type1是key类型,Type2是value类型。

可以通过A[B]=C这种形式赋值,B为Type1,C为Type2。

常用函数

A.clear();

清空

A.empty();

判断是否为空

A.insert(pair<Type1,Type2> (C,D));

插入(不建议这么写)

A.erase(B);

删除,B可以是key值也可以是指针

A.begin(),A.end();

头指针,尾指针

m[x]

哪怕你什么也不干只写一个m[x];也会新建一个点。

因此当你想知道map中是否存在这个映射的时候最好使用m.count(x)。

很多时候可以有效卡常。

multiset和multimap

是可重集合和可重映射。

有两个注意的:第一个是count函数复杂度变成了O(lg(集合大小)+答案)的,也就是如果有很多相同元素,那么count函数代价很大。

第二个是删除x的话,使用s.erase(x)会把所有权值为x的删除。

如果只想删掉一个需要s.erase(s.find(x))。

unordered_set和unordered_map

基于哈希实现的集合和映射。

基本上里面的类型只能是int,long long,double这种非自定义类型。 因为其基于哈希)

在c++11及以后存在,之前没有,乱用可能会CE。

空间常数比较大,时间常数不小,比数组访问慢很多,慎用。

不能顺序遍历,不支持lower_bound。

迭代器

只介绍set/map的迭代器。

bitset

高精度压位二进制。

一个用于处理二进制串的“数组”,在<bitset>里。

所有时间复杂度是线性的操作,常数都是1/32大概。

下标从0开始。

bitset <n> A;//n为长度;

支持所有位运算: << , >> , & , | , ^ ;

常用函数

A.count();

统计1的个数

A.reset();

清0

A.set();

全赋为1

A.size();

返回位数

lower bound()/upper bound()

lower_bound(v.begin(),v.end(),c)可以在一个有序数组当中找出刚好大于或等于c的数,在algorithm库里,可以使用自定义类型,用法与sort相类似。

upper_bound函数类似,这个函数则是在有序数组中找出刚好大于c的数。

next permutation/prev permutation(全排列算法)

algorithm头文件中包含了next_permutation(v.begin(),v.end())和prev_permutation(v.begin(),v.end())两个全排列函数,分别给出一个序列在全排列中的下一个和上一个序列(按字典序),如果存在这样的序列则返回true,不存在则返回false,但仍会得到一个序列。

对于一个任意元素不相同的序列来说,正序排列是最小的排列方式,相应的逆序排列是最大的排列方式,以整数序列{1,2,3}为例,{1,2,3}是第一个排列,{3,2,1}则是最后一个排列,因此序列{1,2,3}没有上一个序列,{3,2,1}没有下一个序列。

pq.swap(pq2)

优先打暴力

考试中经常会遇到想到正确解法却由于写错导致分数挂零的现象,为了应付这种情况,我们可以优先写暴力程序,一来做分数保障,二来可以为对拍做准备。

多贪心原则

当一些题目的正解想不出来,并且一个贪心原则效果不好的情况下,可以采取多个贪心原则同时用,然后取最优的方案。(时间问题一般不用贪心,因为贪心算法时间复杂度往往比较小)

特判

有些时候在想不到正解的情况下,可以通过判断一些特殊情况得到部分分;有些情况虽然想到了正解,却忽略了一些特殊情况,也无法得全分。

打表

有一些题目,看上去非常复杂,但是我们实际上可以通过打表看出规律或者可能性不多,可以直接通过打表得到答案。

补充

基本位运算

位与(&)

给定两个长度相同的二进制串A、B(不同则在前面补0),A与B的结果的每一位由A与B的对应位决定,如果A与B的对应位均为1,则结果的那一位为1,否则为0。

位或(|)

A或B的结果的每一位同样由A与B的对应位决定,如果A或B的对应位为1,则结果的那一位为1,否则为0。

位异或(xor)

A异或B的结果的每一位同样由A与B的对应位决定,如果A与B的对应位不相同,则结果的那一位为1,否则为0。

左移(<<)

a<<b,左移,b以10进制表示。表示将a的最右边(最低位)加上b个0。(相当于乘上了2ⁿ)。

右移(>>)

a>>b,右移,b以10进制表示。表示将a最右面(最低位)的b位删掉。(相当于整除2ⁿ)。

并非原创,仅是整理,请见谅

有关noip基础算法的更多相关文章

  1. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  2. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  3. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. 【网络】-- 网络基础 - 2

    (本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展        是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。    如:有三个人,每个人做的不同的事物,但是是需要协作的完成。    而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据

  6. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  7. ruby - 在 Ruby 中实现 Luhn 算法 - 2

    我一直在尝试用Ruby实现Luhn算法。我一直在执行以下步骤:该公式根据其包含的校验位验证数字,该校验位通常附加到部分帐号以生成完整帐号。此帐号必须通过以下测试:从最右边的校验位开始向左移动,每第二个数字的值加倍。将乘积的数字(例如,10=1+0=1、14=1+4=5)与原始数字的未加倍数字相加。如果总模10等于0(如果总和以零结尾),则根据Luhn公式该数字有效;否则无效。http://en.wikipedia.org/wiki/Luhn_algorithm这是我想出的:defvalidCreditCard(cardNumber)sum=0nums=cardNumber.to_s.s

  8. Ruby 斐波那契算法 - 2

    下面是我写的一个计算斐波那契数列中的值的方法:deffib(n)ifn==0return0endifn==1return1endifn>=2returnfib(n-1)+(fib(n-2))endend它工作到n=14,但在那之后我收到一条消息说程序响应时间太长(我正在使用repl.it)。有人知道为什么会这样吗? 最佳答案 Naivefibonacci进行了大量的重复计算-在fib(14)fib(4)中计算了很多次。您可以将内存添加到您的算法中以使其更快:deffib(n,memo={})ifn==0||n==1returnnen

  9. ruby-on-rails - Rails add_index 算法 : :concurrently still causes database lock up during migration - 2

    为了防止在迁移到生产站点期间出现数据库事务错误,我们遵循了https://github.com/LendingHome/zero_downtime_migrations中列出的建议。(具体由https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in概述),但在特别大的表上创建索引期间,即使是索引创建的“并发”方法也会锁定表并导致该表上的任何ActiveRecord创建或更新导致各自的事务失败有PG::InFailedSqlTransaction异常。下面是我们运行Rails4.2(使用Acti

  10. ruby - 趋势算法 - 2

    我正在开发一个类似微论坛的项目,其中一个特殊用户发布一条快速(接近推文大小)的主题消息,订阅者可以用他们自己的类似大小的消息来响应。直截了当,没有任何形式的“挖掘”或投票,只是每个主题消息的响应按时间顺序排列。但预计会有很高的流量。我们想根据它们引起的响应嗡嗡声来标记主题消息,使用0到10的等级。在谷歌上搜索了一段时间的趋势算法和开源社区应用示例,到目前为止已经收集到两个有趣的引用资料,但我还没有完全理解它们:Understandingalgorithmsformeasuringtrends,关于使用基线趋势算法比较维基百科页面浏览量的讨论,在SO上。TheBritneySpearsP

随机推荐