初赛复习讲义和真题讲解:传送门
组合数学:传送门
博客园的博客
快速排序的三种实现
内部排序和外部排序
tips:
e.g.
16、下列排序算法中,哪些排序是不稳定的()。
A、快速排序 B、选择排序
C、基数排序 D、希尔排序
ans=ABD
12、下列关于排序说法正确的是()。
A、 插入排序、冒泡排序是稳定的
B、 选择排序的时间复杂度为O(n^2)
C、 选择排序、希尔排序、快速排序、堆排序是不稳定的
D、 希尔排序、快速排序、堆排序的时间复杂度为O(nlogn)
ans=ABC
用判定树证明
blog
叶子有 n ! n! n!个,树高 log 2 ( n ! ) = log 2 ( n ) + log 2 ( n − 1 ) + . . . + l o g 2 ( 1 ) < n log 2 n \log_2(n!)=\log_2(n)+\log_2(n-1)+...+log_2(1)
图:
e.g.
17、下列关于排序的算法,不正确的是()。
A.不存在这样一个基于排序码比较的算法:它只通过不超过9次排序码的比较,就可以对任何6个排序码互异的数据对象实现排序。
B.如果输入序列已经排好序,快速排序算法将比归并排序更快。
C.希尔排序的最后一趟就是冒泡排序。
D.任何基于排序码比较的算法,对n个数据对象进行排序时,最坏情况下的时间复杂度不会低于O(nlogn)。
ans=BC
解析:希尔排序的最后一趟是插入排序。
e.g.
设有一个含有13个元素的Hash表(0-12),Hash函数是:H(key)=key%13,其中%是求余数运算。用二次探查法解决冲突,则对于序列(8,31,20,33,18,53,27),则下列说法正确的是()。
A、18在4号格子中 B、33在6号格子中
C、31在5号格子中 D、20在7号格子中
ans=ABCD
e.g.
7、设哈希函数H(K)=3 K mod 11,哈希地址空间为0~10,对关键字序列(32,13,49,24,38,21,4,12),按线性探测法解决冲突的方法构造哈希表,求出等概率下查找失败时的平均查找长度()。
A. 11/11 B. 29/11
C. 35/11 D. 40/11
ans=D
大致思路是先把所有数随便在堆里放好,然后从下往上,每个点的左右子树已经是堆了,就把这个点和左右儿子的堆合并,复杂度 O ( log h ) O(\log h) O(logh),h是当前点到叶子的距离。因为是 O ( log h ) O(\log h) O(logh)而不是 O ( log n ) O(\log n) O(logn),所以是一个差比数列求和得到 O ( n ) O(n) O(n)而不是看起来的 O ( n log n ) O(n\log n) O(nlogn)。
下面是证明:
假设堆的大小为 n n n,高度为 h = log n h=\log n h=logn
那么复杂度就是
T ( n ) = ∑ i = 0 h − 1 ( h − i ) ∗ 2 i = 2 0 ∗ h + 2 1 ∗ ( h − 1 ) + . . . + 2 h − 1 ∗ 1 T(n)=\sum_{i=0}^{h-1}(h-i)*2^i=2^0*h+2^1*(h-1)+...+2^{h-1}*1 T(n)=i=0∑h−1(h−i)∗2i=20∗h+21∗(h−1)+...+2h−1∗1
2 T ( n ) = ∑ i = 0 h − 1 ( h − i ) ∗ 2 i + 1 = 2 1 ∗ h + 2 2 ∗ ( h − 1 ) + . . . + 2 h ∗ 1 2T(n)=\sum_{i=0}^{h-1}(h-i)*2^{i+1}=2^1*h+2^2*(h-1)+...+2^{h}*1 2T(n)=i=0∑h−1(h−i)∗2i+1=21∗h+22∗(h−1)+...+2h∗1
下面减上面,得到
T ( n ) = 2 h − h + ( 2 1 + 2 2 + . . . + 2 h − 1 ) = O ( 2 h ) = O ( n ) T(n)=2^h-h+(2^1+2^2+...+2^{h-1})=O(2^h)=O(n) T(n)=2h−h+(21+22+...+2h−1)=O(2h)=O(n)
常数是2多一点。
看两道题,假如不知道O(n)建堆的话简直**
e.g.
读程序写结果
#include
using namespace std;
int n=12;
char ch[]= {'q','A','S','O','R','T','E','X','A','M','P','L','E'};
void shift(int k,int n) {
char v;
int j;
v=ch[k];
j=k+k;
while(j<=n) {
if(j<n&&ch[j]<ch[j+1])
j++;
if(v<ch[j]) {
ch[j/2]=ch[j];
j*=2;
} else
return;
ch[j/2]=v;
}
}
void hpsrt() {
int k;
char tmp;
for(k=n/2; k>0; k--)
shift(k,n);
cout<<"No.1:";
for(k=1; k<=n; k++)
cout<<ch[k];
cout<<' ';
for(k=n; k>0; k--) {
tmp=ch[1];
ch[1]=ch[k];
ch[k]=tmp;
shift(1,k-1);
}
}
int main() {
int k;
hpsrt();
cout<<"No.2:";
for(k=1; k<=n; k++)
cout<<ch[k];
cout<<endl;
return 0;
}
ans=No.1:XTORSEAAMPLE No.2:AAEELMOPRSTX
解析:就是O(n)建堆,再一个一个从堆里取出来从小到大排序。注意是大根堆并且ch[0]='q’并不用管。
e.g.
已知T(n) = 2*T(n/2) + O(lg n)
求T(n)时间复杂度( )。
A.O (n2) B.O (n logn ) C.O (n) D. O (1)
ans=C
解析:就是建堆的复杂度。然而用主定理并推不出来。
不知道为什么要放在这里,但好像没地方放了。
e.g.
13.现有一段文言文,要通过二进制哈夫曼编码进行压缩。简单起见,假设这段文言文只由4个汉字“之”、“乎”、“者”、“也”组成,它们出现的次数分别为700、600、300、400。那么,“也”字的编码长度可能是( )。
A.1或2 B.2或3 C.3或4 D.1或3
ans=B
解析:
可能是一棵深度为2的完全二叉树,或者一棵深度为3的非叶子节点连成一条链的树。
NOI:全国青少年信息学奥林匹克,1984年开始。自1999年开始,NOI期间,举办同步夏令营和NOI网上同步赛。
NOIP:全国青少年信息学奥林匹克联赛(National Olympiad in Informatics in Provinces简称NOIP)自1995年开始,2019凉凉。
冬令营:全国青少年信息学奥林匹克冬令营(简称冬令营),自1995年起。
APIO: 亚洲与太平洋地区信息学奥赛(Asia Pacific Informatics Olympiad)简称(APIO)2007年创建,该竞赛为区域性的网上准同步赛,是亚洲和太平洋地区每年一次的国际性赛事。
选拔赛:选拔参加国际信息学奥林匹克中国代表队的竞赛(简称选拔赛)。2018年以前叫CTSC,现在叫CTS。
IOI: 出国参加国际信息学奥林匹克竞赛(International Olympiad in Informatics)简称(IOI)。自1989年开始,我国组织参加国际信息学奥林匹克(IOI)竞赛。
CSP-J/S:CCF非专业级软件能力认证(Certified Software Professional Junior/Senior,简称CSP-J/S)创办于2019年,是由CCF统一组织的评价计算机非专业人士算法和编程能力的活动。
计算:
反码是为了解决正负数直接相加的问题,补码则是为了解决-0的问题。
拓展:
e.g.
2、十进制数3 ^ (-2)的位运算结果为()。
A、-3(十) B、1(十) C、1/9(十) D、-2(十)
ans=A
e.g.2
11、二进制小数-0.1011的补码为()。
A、1.1011 B、1.0101 C、10.0101 D、11.0101
ans=B
解析:
浮点数的存储后面有。所以阶码不用管,只要管尾数取补码就好了。
停机问题本质是一高阶逻辑的不自恰性和不完备性,与理发师悖论类似。
不存在一个程序能够判断任意一个程序能否在有限时间内结束运行。
证明:
反证法。
假设存在 P ( H , I ) P(H,I) P(H,I)能够判断任意一个 H H H能否在输入为 I I I时在有限时间内结束运行。
构造一个 Q ( X ) Q(X) Q(X)如下
function Q(X)
{
if (P(X,X)==停机){
while(1){} // 死循环
}
else return 停机
}
那么 P ( Q , Q ) P(Q,Q) P(Q,Q)就会产生悖论。所以 P P P无法满足要求。
e.g.
10、一个无法靠自身的控制终止的循环称为“死循环”,例如,在C语言程序中,语句“while(1) printf(“”);”就是一个死循环,运行时它将无休止地打印号。下面关于死循环的说法中,只有()是正确的。
A、不存在一种算法,对任何一个程序及相应的输入数据,都可以判断是否会出现死循环,因而,任何编译系统都不做死循环检验。
B、有些编译系统可以检测出死循环。
C、死循环属于语法错误,既然编译系统能检查各种语法错误,当然也应该能检查出死循环。
D、死循环与多进程中出现的“死锁”差不多,而死锁是可以检测的,因而,死循环也可以检测的。
ans=A
香农
p.s.计算机之父--冯诺依曼(详见冯诺依曼理论)
是由英特尔(Intel)创始人之一戈登·摩尔(Gordon Moore)提出来的。其内容为:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。
现已经放缓至约三年翻一番。
1、计算机硬件设备由存储器、运算器、控制器、输入设备和输出设备5部分组成。
2、存储程序思想——把计算过程描述为由许多命令按一定顺序组成的程序,然后把程序和数据一起输入计算机,计算机对已存入的程序和数据处理后,输出结果。
美籍匈牙利科学家冯·诺依曼最先提出程序存储的思想,并成功将其运用在计算机的设计之中,根据这一原理制造的计算机被称为冯·诺依曼结构计算机,由于他对现代计算机技术的突出贡献,因此冯·诺依曼又被称为“计算机之父”。
e.g.
12、下列属于冯诺依曼计算机模型的核心思想有()。
A、采用二进制表示数据和指令 B、采用“存储程序”工作方式
C、计算机硬件有五大部件(运算器、控制器、存储器、输入和输出设备)
D、结构化程序设计方法
ans=ABC,与程序设计无关
阿达·洛芙莱斯(Ada Lovelace),解伯努利方程,建立循环和子程序概念
性能指标:字长、主频(register寄存器:CPU的组成部分,速度最快)
第一块4位微处理器intel 4004
传送到百度百科
Foxpro,Access,Oracle,Sybase,DB2和Informix则是数据库系统。
内存(速度由小到大排序):ROM(只读,装入整机前写好并不能更改),RAM(用来暂时存储程序、数据和中间结果),cache(高速缓冲存储器),寄存器
外存:硬盘,闪存(又名优盘。。。)
显存:显卡内存,是暂时存储图像信息类似内存的存在
问:BIOS在哪里?
答:在ROM。
e.g.
12.下列说法中,正确的是( )。
A)数据总线决定了中央处理器CPU所能访问的最大内存空间的大小。
B)中央处理器CPU内部有寄存器组,用来储存数据。
C)不同厂家生产的CPU所能处理的指令集是相同的。
D)数据传输过程中可能会出错,奇偶校验法可以检测出数据中那一位在传输中出了差错。
ans=B
解析:
B 寄存器上面有累加器,但是不妨碍他的存储功能呀。。。
D 不能知道具体是哪一位,只能知道有没有错。
1.ASCII码(American Standard Code for Information Interchange):一个字节,0x00~0x7f。0 ~33控制字符,33之后可见(48-'0’,65-‘A’,97-‘a’)
2.汉字:两个字节,每个0x80~0xff。国标码,处理码,输出码
图片存储:(24/32位真彩色)颜色位数×图片大小
视频存储:图片容量×帧频×时间
音频存储:频率×量化位数×时间×声道数(bit)
e.g.
1.以下关于编程语言的选项中,正确的是( )
A.操作系统控制和管理软硬件资源,需要直接操作机器,所以需要用机器语言编写
B.同样一段高级语言程序通过不同的编译器可能产生不同的可执行程序
C.C++是一种纯面向对象语言
D.解释执行的语言只能选择一个解释器。
ans=B
是机器语言的术语。
操作码指计算机程序中所规定的要执行操作的那一部分指令或字段(通常用代码表示),其实就是指令序列号,用来告诉CPU需要执行哪一条指令。
操作数是运算符作用于的实体,是表达式中的一个组成部分,它规定了指令中进行数字运算的量 。表达式是操作数与操作符(码)的组合。
双操作数又称为源操作数(source)和目的操作数(destination)。
e.g.
6.计算机能直接执行的指令包括两部分,它们是( )
A.源操作数与目标操作数 B. 操作码与操作数
C.ASCII码与汉字代码 D. 数字与字符
ans=B(看过就会了)
了解尾数、基数、阶码存储
传送到博客
字长是用来表示一次性处理事务的一个固定长度的位(bit)的位数
虽然系统总线于1970年代至1980年代广受欢迎,但是现代的计算机却使用不同的分离总线来做更多特定需求用途。
系统总线上传送的信息包括数据信息、地址信息、控制信息,因此,系统总线包含有三种不同功能的总线,即数据总线DB(Data Bus)、地址总线AB(Address Bus)和控制总线CB(Control Bus)。
e.g.
8.计算机系统总线上传送的信号有( )
A. 地址有信号与控制信号 B. 数据信号、控制信号与地址信号
C. 控制信号与数据信号 D. 数据信号与地址信号
ans=B
地理范围:局域网(LAN,Local Area Net),城域网(MAN,Metropolitan),广域网(WAN,Wide)
p.s.以太网是一种局域网
e.g.
ans=ABC
解析:
GPRS(General Packet Radio Service)
以太网是目前应用最普遍的局域网技术(有线)
plus.html-超文本标记语言[HyperText Markup Language]
与TCP/IP的关系
各种协议所处阶层
简化版
重点:TCP协议在传输层(从下到上第4层),IP协议在网络层(从下到上第3层)
比较不错的博客
看图,根据32位前端连续1的个数来区分。
也就是说,分为以下几类:
A: 0.0.0.0-127.255.255,其中段0和127不可用
B: 128.0.0.0-191.255.255.255
C: 192.0.0.0-223.255.255.255
D: 224.0.0.0-239.255.255.255
E: 240.0.0.0-255.255.255.255,其中段255不可用
这其中除了段0和段127之外,还有一些IP地址因为有其他的用途,是不可以用作普通IP的。还有一部分被用作私有IP地址。
ipv4是32位(4*8),ipv6是128位。
Internet提供了以下4种服务:
1.Internet上提供了高级浏览WWW服务
WWW(World Wide Web)服务是一种建立在超文本基础上的浏览、查询因特网信息的方式,它以交互方式查询并且访问存放于远程计算机的信息,为多种因特网浏览与检索访问提供一个单独一致的访问机制。Web 页将文本、超媒体、图形和声音结合在一起。因特网给企业带来通信与获取信息资源的便利条件。
2.Internet上提供了电子邮件E-mail服务
3.Internet上提供了远程登录Telnet服务
Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。
4.Internet上提供了文件传输FTP服务
FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。
百度百科说:
域名(英语:Domain Name),简称域名、网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。
网域名称系统(DNS,Domain Name System,将域名和IP地址相互映射的一个分布式数据库)是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网,而不用去记住能够被机器直接读取的IP地址数串。
简单说域名就是IP地址的别名,DNS就是一个在IP和域名之间映射的函数。
e.g.
6.关于计算机网络,下面的说法哪些是正确的:
A)网络协议之所以有很多层主要是由于新技术需要兼容过去老的实现方案。
B)新一代互联网使用的IPv6标准是IPv5标准的升级与补充。
C)TCP/IP是互联网的基础协议簇,包含有TCP和IP等网络与传输层的通讯协议。
D)互联网上每一台入网主机通常都需要使用一个唯一的IP地址,否则就必须注册一个固定的域名来标明其地址。
ans=C
解析:
A 显然是有实用的目的的。比如灵活性好,易于维护。
B IPV4
D “否则”错了,IP地址是必须的,而域名不是必须的。
基本的网络设备有:计算机(无论其为个人电脑或服务器)、集线器、交换机、网桥、路由器、网关、网络接口卡(NIC)、无线接入点(WAP)、打印机和调制解调器、光纤收发器、光缆等。
e.g.
ans=A
解析:
光驱,disk driver,光盘驱动,电脑用来读写光碟内容的机器
平常根本不用的说,都不知道他有什么存在的意义。。。
先看一道毒瘤题:
#include
using namespace std;
int main() {
int a,k;
a=5;
k=8;
char l='a';
while(a>0) {
switch(l+=a--) {
case 'j':
case 'l':
l-=a;
case 'f':
a--;
continue;
case 'm':
case 'p':
l++;
break;
default:
k++;
case 'o':
k-=2;
continue;
}
k++;
l--;
}
cout<<l<<k<<endl;
return 0;
}
1.程序输出为()
A. r6 B. l6 C. k8 D.i12
2.如果第一个continue改为break了,答案为i8是否正确?()。
A. 正确 B. 错误
想要做此题,就有必要知道switch的运作方式。
大致如下:
case
的常量,与switch
的值比较,不相等则跳过这个case继续比较,相等则进入2。switch
结束的所有语句,除非有break
退出了switch
。注意:
switch
只能用break
,与continue
无关。上面程序的continue
是针对while
循环而并非用来跳出switch
。知道continue
和break
的用处,就可以轻松地做这题了。
答案是B; B。第二题的正确输出应该是h9
。
18、以下程序的输出结果是( )
int main(){
char st[20]= "hello\0\t\\";
printf("%d %d \n",strlen(st),sizeof(st));
}
A.12 20 B. 5 20 C. 20 20 D. 5 5
注意\0
是char数组的结尾符号,所以在算strlen
的时候到\0
就结束了。
p.s.\\
表示一个斜杠,如果单独的\
是会报错的。
上一个程序
#include
#include
#include
using namespace std;
void fun1(int *x, int *y) // 交换两个参数指针,但是不影响函数外面的值
{
int *k;
k = x;
x = y;
y = k;
}
void fun2(int *x, int *y) // 相当于引用,交换两个参数指针所指的值
{
int k;
k = *x;
*x = *y;
*y = k;
}
void fun3(int &x, int &y) // 引用,x和y是int类型而不是*int
{
int k;
k = x;
x = y;
y = k;
}
void fun4(int *x, int *y) // 等于fun2()
{
int *k;
k = new int;
*k = *x;
*x = *y;
*y = *k;
}
int &fun5() // fun5()的返回值引用了xa,所以修改fun5()就修改了xa,并且静态变量xa会保持之前修改过的值
{ // 这里static不能去掉,去掉就会warning,因为一般局部变量在退出函数的时候是会清除的
static int xa = 10;
return xa;
}
int main()
{
int a = 233, b = 666;
int *x1 = &a, *y1 = &b;
int &x3 = a, &x4 = b; // 引用
fun1(x1, y1);
cout << a << " " << b << endl;
cout << *x1 << " " << *y1 << endl;
cout << "----------------------------" << endl;
fun2(x1, y1);
cout << a << " " << b << endl;
cout << *x1 << " " << *y1 << endl;
cout << "----------------------------" << endl;
fun3(a, b);
cout << a << " " << b << endl;
cout << *x1 << " " << *y1 << endl;
cout << "----------------------------" << endl;
fun4(x1, y1);
cout << a << " " << b << endl;
cout << *x1 << " " << *y1 << endl;
cout << "----------------------------" << endl;
cout << fun5() << endl;
fun5() = 30;
cout << fun5() << endl;
return 0;
}
/*
输出:
233 666
233 666
----------------------------
666 233
666 233
----------------------------
233 666
233 666
----------------------------
666 233
666 233
----------------------------
10
30
*/
如何方便地记住优先级:
容易记错的是 "<<“和”>>“优于”&“优于”^“优于”|"
上面是位运算优先级,逻辑运算优先级是 “非┐” > “和∧” > “或∨” > “蕴含→” > "双向蕴含←→"
贴个代码,方便理解:
#include
#include
#include
using namespace std;
//运算符优先级
int main()
{
//"!" "~" 优先于 加减乘除 优先于 "<<" ">>" 优先于 ">" "<" ">=" "<=" 优先于 "==" "!="
//优先于 "^" 优先于 "&" 优先于 "|" 优先于 "&&" 优先于 "||" 优先于 "?:" 优先于 "赋值"
//验证
//"~" > "<<"
int a = ~1>>1;
cout << a << endl;
a = 1<<~0xffffffff;
cout << a << endl;
//"<<" 和 ">>" 同级
a = 1<<2>>1;
cout << a << endl;
a = 8>>1<<1;
cout << a << endl;
//"<<" > "&"
a = 8&4<<1;
cout << a << endl;
//"&" > "^"
a = 2&3^3;
cout << a << endl;
//"^" > "|"
a = 1&3|2&6;
cout << a << endl;
return 0;
}
23 | 2 ^ 5 = 23
而不是18。(1 << i) <= dpt[y] - dpt[x]
int mid = l + r >> 1
有几种看起来并不常见的运算符:
P→Q条件
举个例子:一个姓胡的人说:“如果太阳从西边升起,我就不姓胡。”这句话是对的,因为太阳不会从西边升起,P=False,所以不论Q是否正确,这句话都是正确的。而这里的“如果”并不表示假设,而是表示判断。
双条件
即等价命题。
e.g.
看到了就算一算。
12、若A=False,B=True,C=True,D=False,则下列逻辑运算真的有()
A、(A∧B)V(C∧D) B、((A∧¬B)VD)∧B
C、((¬A→¬B)∧C)VD D、¬A∧(¬D→¬C)→¬B
ans=D
解析:D.假作为条件得到一定是真。
巨佬博客:styx
e.g.1
2012.20.以下关于计算复杂度的说法中,正确的有( )。
A.如果一个问题不存在多项式时间的算法,那它一定是NP类问题
B.如果一个问题不存在多项式时间的算法,那它一定不是P类问题
C.如果一个问题不存在多项式空间的算法,那它一定是NP类问题
D.如果一个问题不存在多项式空间的算法,那它一定不是P类问题
ans=BD
解析
A 还记得图灵停机问题吗?那是一个NP-hard而不是NP问题
B 这是对的,因为P问题的定义是存在多项式时间的复杂度算法
C、D 时间复杂度和空间复杂度都是复杂度,是等价的。
e.g.2
2013.14. ( )属于 NP 类问题。
A. 存在一个 P 类问题
B. 任何一个 P 类问题
C. 任何一个不属于 P 类的问题
D. 任何一个在(输入规模的)指数时间内能够解决的问题
ans=AB
解析
这里考察的知识点是NP问题的定义以及P∈NP
由之前可知任何P问题都是NP问题,同理一定存在一个P问题是NP问题 AB都是对的 C的话我们可以继续用万能的停机问题去反驳他
D考察的是定义,指数时间内能解决不意味着多项式时间能验证解,所以D是错的
e.g.
20、定义f(x):若x的二进制表示中有连续两个1,则f(x)=0,反之f(x)=1。定义g(x)=f(0)+f(1)+f(2)+…+f(x),则g(233)=___________。
A. 31 B. 43 C. 55 D. 76
ans=C
解析:
首先发现一下g(233)=g(255),然后暴力枚举放几个1,排列组合算一下。
a n s = 1 + C 8 1 + C 7 2 + C 6 3 + C 5 4 = 55 ans=1+C_8^1+C_7^2+C_6^3+C_5^4=55 ans=1+C81+C72+C63+C54=55
e.g.
19.平面上有三条平行直线,每条直线上分别有7,5,6个点,且不同直线上三个点都不在同一条直线上。问用这些点为顶点,能组成多少个不同四边形?
A)1095 B)1620 C)2250 D) 4500
ans=C
解析:
就是在这些点里面选4个点,但是同一条直线最多只能选2个。
所以枚举选点个数:1) 2 + 2,2) 2 + 1 + 1。然后排列组合计算。
e.g.
3、给定4个变量x1,x2,x3,x4,满足1<=xi<=10,求方程x1+x2+x3+x4=30的解数()。
A.282 B.288 C.360 D.362
ans=A
此题也可以DP做,在下面有写。千万不要容斥,不如DP。
解析:
设 y i = 10 − x i ( i ∈ [ 1 , 4 ] ) y_i=10-x_i(i\in[1,4]) yi=10−xi(i∈[1,4]),那么 y 1 + y 2 + y 3 + y 4 = 10 , ( y i ∈ [ 0 , 9 ] ) y_1+y_2+y_3+y_4=10,(y_i\in[0,9]) y1+y2+y3+y4=10,(yi∈[0,9])。
直接暴力算出没有上界限制的方案数 C 13 3 = 286 C_{13}^3=286 C133=286,然后减去某一项 y i y_i yi为10的数量 4 4 4,答案为 282 282 282。
前几项为:1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, …
- 一个足够大的栈的进栈序列为1,2,3,⋯,n时有多少个不同的出栈序列?
枚举最后一个出栈的数字 k k k。那么 k k k从入栈起就必须压在栈底直到最后弹出。所以在他前面进栈的必须在他进栈之前弹空,方案数 c a t k − 1 cat_{k-1} catk−1。在他后面进栈的必须在最后他弹出之前弹空,方案数 c a t n − k cat_{n-k} catn−k。在 k k k之前和之后的互不影响。
所以得到递推式 c a t i = ∑ j = 0 j ≤ i − 1 c a t j ∗ c a t i − 1 − j cat_i=\sum_{j=0}^{j \leq i-1} cat_j*cat_{i-1-j} cati=∑j=0j≤i−1catj∗cati−1−j,就是卡特兰数。
变种:n个人拿5元、n个人拿10元买物品,物品5元,老板没零钱。问有几种排队方式。
- 二叉树构成问题。有n个结点,问总共能构成几种不同的二叉树。
枚举根的左儿子右儿子数量递推,发现就是卡特兰数递推式。
- 凸多边形的三角形划分。一个凸的n边形,用直线连接他的两个顶点使之分成多个三角形,每条直线不能相交,问一共有多少种划分方案。
选一个基准点,连出一条边,可以把 n n n边形分成两块,一块大小为 i ( 3 ≤ i ≤ n − 1 ) i(3 \le i \le n-1) i(3≤i≤n−1),另一块 n − i + 2 n-i+2 n−i+2
发现是卡特兰数递推式。
变式:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?
- 在n*n的格子中,只在下三角行走,每次横或竖走一格,有多少中走法?
其实向右走相当于进栈,向左走相当于出栈,本质就是n个数出栈次序的问题,所以答案就是卡特兰数。
- n个长方形填充一个高度为n的阶梯状图形的方法个数?
把包含左上角的矩形去掉分出两块小阶梯,发现就是卡特兰数递推式。
第二维比较小的时候可以矩阵快速幂优化(雾)。
若递推时间复杂度
T ( n ) = a T ( n / b ) + O ( n d ) T(n) = a T(n / b) + O(n ^ d) T(n)=aT(n/b)+O(nd)
那么
T ( n ) = { O ( n l o g b a ) , d < l o g b a Θ ( n d l g n ) , d = l o g b a Ω ( n d ) , d > l o g b a T(n)=\left\{ \begin{aligned} &O(n^{log_b a}) & ,d < log_b a\\ &\Theta(n^dlgn) & ,d = log_b a\\ &\Omega(n^d) & ,d > log_b a \end{aligned} \right. T(n)=⎩⎪⎪⎨⎪⎪⎧O(nlogba)Θ(ndlgn)Ω(nd),d<logba,d=logba,d>logba
首先,背下来。
其次,努力理解。
对于第二种情况,很容易就推出来了, O ( n l o g b a l o g b n ) O(n^{log_ba} log_bn) O(nlogbalogbn)。
第一种和第三种情况以现在的水平还无法推导,只能感性理解。
第一种情况,f(n)的复杂度比较小,底数小于0,指数大起来之后求和趋近于1,于是 O ( n l o g b a ) O(n^{log_ba}) O(nlogba)。
第三种情况,f(n)复杂度大,底数大于0,复杂度趋向于f(n), O ( f ( n ) ) O(f(n)) O(f(n))
等以后能理解了再写博客,三天之后要是用到了就背结论。
upd.根据Tiandong123的概括和升华,主定理就是谁大听谁的。
e.g.
简单应用
4、若某算法的计算时间表示为递推关系式:
T(N) = 2T(N / 4) + N^(1/2)
T(1) = 1
则该算法的时间复杂度为( )。
A. O(N^(1/2)) B. O(N log N) C. O(N^(1/2)logN) D. O(N^2)
ans=C
解析: a = 2 , b = 4 , d = 1 2 , l o g b a = d , ∴ T ( n ) = Θ ( n d l g n ) = Θ ( n l o g 2 n ) a=2,b=4,d=\frac{1}{2},log_ba=d,\therefore T(n)=\Theta(n^dlgn)=\Theta(\sqrt{n}log_2n) a=2,b=4,d=21,logba=d,∴T(n)=Θ(ndlgn)=Θ(nlog2n)
详见XJOI各种放球题目。
最难理解的是球同盒同的情况(可空或非空差不多)。相当于整数拆分。
递推方程: f [ i ] [ j ] = f [ i − j ] [ j ] + f [ i ] [ j − 1 ] f[i][j]=f[i-j][j]+f[i][j-1] f[i][j]=f[i−j][j]+f[i][j−1],边界 f [ 0 ] [ j ] = 1 ( j ≥ 0 ) f[0][j]=1(j \geq 0) f[0][j]=1(j≥0)。
意义是把所有盒子按照球的个数降序排序(以去重),可以YY每一种方案都可以被给一些前缀放球构造出来,所以对于 f [ i ] [ j ] f[i][j] f[i][j]可以选择给前 j j j个盒子全都放一个球,或者新开一个盒子。
其实这个方程可以看成前缀和,把前缀和优化掉的那一维展开之后,
方程: f [ i ] [ j ] = ∑ k = 1 k ≤ j f [ i − j ] [ k ] f[i][j]=\sum_{k=1}^{k \leq j}f[i-j][k] f[i][j]=∑k=1k≤jf[i−j][k],解释起来很难啊,可以把这个方程想成交换了x轴和y轴,给每一层(这个层就是原来方程的某一次放球)放球,最多放了前 j j j个盒子。
这个 f [ i ] [ j ] = f [ i − j ] [ j ] + f [ i ] [ j − 1 ] f[i][j]=f[i-j][j]+f[i][j-1] f[i][j]=f[i−j][j]+f[i][j−1]呢,还可以看成用不大于 j j j的整数组成 i i i的方案数,自行YY。
再记一下球不同盒同非空的情况。
方程: f [ i ] [ j ] = f [ i − 1 ] [ j ] ∗ j + f [ i − 1 ] [ j − 1 ] f[i][j]=f[i-1][j]*j+f[i-1][j-1] f[i][j]=f[i−1][j]∗j+f[i−1][j−1], f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个球,放在 j j j个盒子里的方案数。意义是对于当前的球,可以单独开一个新格子,或者放在已经有球的任意一个盒子里。因为盒子是相同的,可以YY成不同的盒子按照盒子里标号最小的那个球的序号升序排序。
问题如下:
假设你正在参加一个游戏节目,你被要求在三扇门中选择一扇:其中一扇后面有一辆车;其余两扇后面则是山羊。你选择了一道门,假设是一号门,然后知道门后面有什么的主持人,开启了另一扇后面有山羊的门,假设是三号门。他然后问你:“你想选择二号门吗?你决定根据打开一号门三号门获得汽车的概率做出选择,那么选择三号门获得汽车的概率是()。
A.1/6
B. 1/3
C. 1/2
D. 2/3
感觉好像是 1 2 \frac12 21,开门之后一号和三号门开出车的概率应该一样才对。
然而事实并非如此。
讨论一下,分两种情况:
所以换门拿车的总概率是 1 3 ∗ 0 + 2 3 ∗ 1 = 2 3 \frac13*0+\frac23*1=\frac23 31∗0+32∗1=32。
csdn博客
洛谷博客
这两篇讲的不错,还是人能看得懂的。
结论记一记: C n m = ∏ i = 0 i ≤ l o g p n C n [ i ] m [ i ] C_{n}^{m}=\prod_{i=0}^{i \leq log_p^n}C_{n[i]}^{m[i]} Cnm=∏i=0i≤logpnCn[i]m[i],其中m[i]和n[i]是m和n在p进制下每一位的数字。
公元元年1月1日是星期一,明明一点用都没有。
公式不用像网上那样,化着化着失去了他本来的样子,按照日期安排的规律去推也很快。
dd[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
cin >> y >> m >> d;
ans = ((y-1)+(y-1)/4-(y-1)/100+(y-1)/400
+dd[m-1]
+d
+(((y%4 == 0 && y%100 != 0) || (y%100 == 0 && y%400 == 0)) && m >= 2))%7;
比如2018年10月13日初赛是星期六,1926年8月17日somebody的生日是星期二 蛤蛤蛤
简称
B - Binary - 二进制
O - Octet - 八进制
D - Decimal - 十进制
H - Hex - 十六进制
斜着走,到边界跳回第一个,被填掉了就去上方。
4 | 9 | 2 |
---|---|---|
3 | 5 | 7 |
8 | 1 | 6 |
百度百科
分两种情况讨论。
数数题最好玩了。
e.g.
3、给定4个变量x1,x2,x3,x4,满足1<=xi<=10,求方程x1+x2+x3+x4=30的解数()。
A.282 B.288 C.360 D.362
ans=A
d p [ i ] [ j ] dp[i][j] dp[i][j]表示前i个变量,和为j的方案数。
先写出递推式 d p [ i ] [ j ] = d p [ i − 1 ] [ j − k ] ( k ∈ [ 1 , 10 ] , j − k ≥ 0 ) dp[i][j]=dp[i-1][j-k](k\in[1,10],j-k\ge 0) dp[i][j]=dp[i−1][j−k](k∈[1,10],j−k≥0)
边界 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1
列表爆算开始:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||||||||||
2 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 63 | 55 | 45 | 36 | 28 | 21 | 15 | 10 | 6 | 3 | 1 | |||||||||||||||||||
4 | 282 |
没有写的都是不需要算的。其实计算量不大。
此题也可以容斥来做。此题也可以直接组合数做。
初赛的概率期望DP一般不会太难,但是要尽量减少算法复杂度,比如用作差,方程移项
状态最好按照倒推的思路去设计,因为需要一个走到就停止的终止状态作为递推的初始值。如果正推,最好把状态设计成从起点第一次到达某个位置的期望。
例题一道:
一只青蛙,从5开始跳,每次等概率跳到自己或者比自己编号小的位置,跳到1的时候停止,求从5跳到1的期望步数。
状态: f [ i ] f[i] f[i]表示从i跳到0的期望。
初始状态: f [ 1 ] = 0 f[1]=0 f[1]=0
转移方程: f [ i ] = ∑ j = 1 j ≤ i f [ j ] i + 1 f[i]=\frac{\sum_{j=1}^{j\leq i}f[j]}{i}+1 f[i]=i∑j=1j≤if[j]+1(这里j<=i)
移项: f [ i ] = ∑ j = 0 j < i f [ j ] i − 1 + i i − 1 f[i]=\frac{\sum_{j=0}^{j < i}f[j]}{i-1}+\frac{i}{i-1} f[i]=i−1∑j=0j<if[j]+i−1i(这里j 所以: f [ 1 ] = 0 , f [ 2 ] = 2 , f [ 3 ] = 5 / 2 , f [ 4 ] = 17 / 6 , f [ 5 ] = 37 / 12 f[1]=0,f[2]=2,f[3]=5/2,f[4]=17/6,f[5]=37/12 f[1]=0,f[2]=2,f[3]=5/2,f[4]=17/6,f[5]=37/12,答案为37/12
tips: 欧拉图不一定要联通,可以有度为0的点被孤立,因为只要求经过每条边。但满足没有奇数度的定点的图不一定是欧拉图,因为可以分成多个不联通的欧拉图。
毕竟是初赛,记几个结论就好了。。。
1.设G具有n个结点的简单图,如果G中每一对结点度数之和大于等于n-1,则在G中存在一条汉密尔顿路。
e.g.
把不能在同一时间考的科目连边,连边的两点不能同色,求最小染色数。
#include
using namespace std;
int a,b,c,d,e;
int main() {
a=1;b=a+2;c=b+3;d=c+4;a=d+5;
for(int e=1;e<=4;e++){
if(a%2==0) a=a/2+d;
else a=a/2+d+1;
b=a+2;c=b+3;d=c+4;a=d+5;
}
cout<<a<<endl;
return 0;
}
ans=152
#include
using namespace std;
int x, y;
long long tot;
int f(int x, int y) {
tot++;
int ans = y;
for (int i = 1; i <= y - x; ++i)
ans += x / i * f(x + i, y - i);
return ans;
}
int main() {
scanf("%d%d", &x, &y);
printf("%d\n", f(x, y));
}
3、若输入:2 10,则输出为()。
A. 278 B.713 C.1112 D.1653
4、程序结束,tot的值为29,请判断结果是否正确()。
A、正确 B、错误
ans=D;B
列表手膜:
首先是答案:
(x,y) | ans |
---|---|
(10,2) | 2 |
(9,3) | 3 |
(8,4) | 4 |
(7,5) | 5 |
(6,6) | 6 |
(5,7) | 7 + 5 * 6 + 2 * 5 = 47 |
(4,8) | 8 + 4 * 47 + 2 * 6 + 1 * 5 + 1 * 4 = 217 |
(3,9) | 9 + 3 * 217 + 1 * 47 + 1 * 6 = 713 |
(2,10) | 10 + 2 * 713 + 1 * 217 = 1653 |
然后是tot:
(x,y) | tot |
---|---|
(10,2) | 1 |
(9,3) | 1 |
(8,4) | 1 |
(7,5) | 1 |
(6,6) | 1 |
(5,7) | 1 + 1 + 1 = 3 |
(4,8) | 1 + 3 + 1 + 1 + 1 = 7 |
(3,9) | 1 + 7 + 3 + 1 + 1 + 1 + 1 = 15 |
(2,10) | 1 + 15 + 7 + 3 + 1 + 1 + 1 + 1 + 1 = 31 |
#include
using namespace std;
int a[10000];
int gcd(int m,int n){
while(n){
int r=m%n;m=n;n=r;
}
return m;
}
int main(){
int n=1000,r=202;
for(int i=1;i<=n-r;i++) a[i]=n-i+1;
for(int i=2;i<=r;i++){
int k=i;
for(int j=1;j<=n-r;j++)
if(gcd(k,a[j])>1){
int g=gcd(k,a[j]);
k/=g; a[j]/=g;
if(k==1) break;
}
}
int p=1,g=0;
for(int i=1;i<=n-r;i++){
p*=a[i];
while(p%5==0){
p/=5; g++;
}
p%=5;
}
cout<<g<<endl;
return 0;
}
ans=151
解析:
ans=203到1000中因数5的个数-1到202中因数的个数
=1到1000中因数5的个数-2*(1到202中因数的个数)
#include
using namespace std;
int n,ans=0;
int dfs(int row,int ld,int rd) {
int lim=(1<<n)-1;
int p,pos;
if(row!=lim) {
pos=lim&~(row|ld|rd);
while(pos) {
p=pos&-pos;
pos^=p;
dfs(row|p,(ld+p)<<1,(rd+p)>>1);
}
} else ans++;
}
int main() {
cin>>n;
dfs(0,0,0);
cout<<ans;
}
9、输入为8,结果为()?
A. 40 B. 67 C. 92 D. 108
假题,看看就好了。
ans=C
解析很难看出这是一个八皇后。。。就算你看出来了,也数不出来。反正也就6分,蒙一个,期望得分1.5分。
1、 现在政府计划在某个区域的城市之间建立高速公路,以使得其中任意两个城市之间都有直接或间接的高速公路相连。费用为每千米一个单位价格,求最小费用。
输入:n(n<=100,表示城市数目)
接下来n行每行两个数xi,yi,表示第i个城市的坐标。(单位:千米)
输出:最小费用(保留两位小数)
#include
#include
#include
using namespace std;
const int MaxN=101;
struct Tcity{
double x,y;
}c[MaxN];
double d[MaxN][MaxN];
int p[MaxN];
inline double pingfang(double a){return (1);}
int main() {
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>c[i].x>>c[i].y;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=sqrt(pingfang(c[i].x-c[j].x)+pingfang( (2) ));
p[1]=0;
for(int i=2;i<=n;i++) (3);
double ans=0;
for(int i=1;i<n;i++){
double Min=1e10;
int k;
for(int j=1;j<=10;j++)
if( (4) ){Min=d[p[j]][j]; (5) ;}
ans+=d[p[k]][k];
p[k]=0;
for(int j=1;j<=n;j++) if( (6) )p[j]=k;
}
cout<<fixed<<setprecision(2)<<ans<<endl;
return 0;
}
ans:
a*a
c[i].y-c[j].y
p[i]=1
p[j]>0&&d[p[j]][j]
d[k][j]
< AGC >
Ringo Mart是一家便利店,出售苹果汁。
在Ringo Mart开业当天,早上有A罐罐装果汁。Snuke每天在白天购买B罐果汁。 然后,Ringo Mart的经理每晚检查剩余库存的果汁罐数。如果有C或更少的罐头,D新罐将在第二天早上添加到库存中。
确定Snuke是否可以无限期购买果汁,也就是说,当他试图购买果汁时,总会有B罐或更多罐装果汁。除了Snuke之外,没有人在这家商店买果汁。
请注意,此问题中的每个测试用例都包含T个查询。
输入格式
第一行T,接下来T行每行A,B,C,D。
输出:对每个询问,若可以,输出“Yes”,否则输出“No”。
#include
using namespace std;
#define ll long long
int T; ll a, b, c, d;
int main(){
for ( cin >> T ; T--; ){
cin >> a >> b >> c >> d;
if ( (1) || (2)){
puts("No");
continue;
}
a (3) b;
a (4) d;
ll g = __gcd(b , d);
puts( (5) / g < (b – 1 – a + d) / g? "No": "Yes");
}
}
1、A. c>d B. a<b
2、A. c<a B. d < b C. a<c D. c<b
3、A. *= B. %= C. /= D. -=
4、A. *= B. %= C. /= D. -=
5、A. (c+b-d) B. (c-b+d) C. (c-a+d) D. (c+b-a)
ans:
BBDBC
问题描述:
对于任意给出的一个正整数n(n<=50000),求出具有n个不同因子的最小正整数m。(例如:但n=4时m=6,因为6有4个不同整数因子:1、2、3、6,并且是最小的有4个因子的整数。)
程序:
#include
#include
#include
#include
using namespace std;
#define maxn 50000
long p[17]= {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
long f[501],c[501],minc[501],m[20000];
long h,i,j,k,l,n,t,total;
float mins,s;
void resolve(long dep,long m,long r) {
long i;
if (r==1) {
s=0;
for (i=1; i<=dep-1; i++) s=s+ ① ;
if (s < mins) {
mins=s;
l=dep-1;
for (i=0; i<=500; i++) minc[i]=c[i];
}
} else {
for (i=m; i<=total; i++)
if ( ② ) {
c[dep]=f[i];
resolve(dep+1, ③ );
}
}
}
void mul(long t) {
for(k=0; k<=h; k++) m[k]=m[k]*t;
for(k=0; k<=h; k++) {
m[k+1]=m[k+1]+m[k]/10;
m[k]=m[k] % 10;
}
while ( ④ ) {
m[h+2]=m[h+1] / 10;
m[h+1]=m[h+1] % 10;
++h;
}
}
int main() {
scanf("%ld",&n);
total=0;
for (i=n; i>=2; i--)
if (n % i == 0) {
++total;
f[total]=i;
}
mins=LONG_MAX;
resolve(1,1,n);
for (i=1; i<=15000; i++) m[i]=0;
m[0]=1;
h=0;
t=1;
for (i=1; i<=l; i++)
for (j=1; j<=minc[i]-1; j++)
if (p[i]<2000000000 / t)
t=t*p[i];
else {
mul(t);
⑤ ;
}
mul(t);
for (i=h; i>=0; i--) printf("%ld",m[i]);
printf("\n");
return 0;
}
1、A. c[i]*log(p[i]) B. (c[i]-1)*log(p[i]) C. c[i]*p[i] D. c[i]*log(p[i]+1)
2、A. f[i]%r==0 B. f[i]%r!=0 C. r%f[i]==0 D. r%f[i]!=0
3、A. i,r%c[dep] B. i+1,r/c[dep] C. i+1,r%f[i] D. i,r/f[i]
4、A. m[h+2]>0 B. m[h+1]>0 C. m[h] D. k<total
5、A. m[i]=p[i] B. m[i]=t*p[i] C. t=p[i] D. t=t*p[i]
ans:
B; C; D; B; C
题意很简单。
因为 m = ∑ p i x i m=\sum p_i^{x_i} m=∑pixi的因数个数为 n = ∏ ( x i + 1 ) n=\prod (x_i+1) n=∏(xi+1),所以爆搜 n = ∏ c i n=\prod c_i n=∏ci的拆分。
要使最终数尽量小,就把较大的 c i c_i ci分给较小的 p i p_i pi,得到 m = ∏ p i c i − 1 m=\prod p_i^{c_i-1} m=∏pici−1。
因为爆long long无疑,所以比较大小的时候改用log,即比较 log m = ∑ ( c i − 1 ) ∗ log p i \log m = \sum (c_i-1)*\log p_i logm=∑(ci−1)∗logpi。
最后求答案,不一次一次的高精乘,而是把乘的数存在 t t t里,在 t t t快要爆的时候高精乘。
注意检查。
能拿的分尽量拿,没听说过的计算机基础知识排除完了尽量蒙。
不要看着B选C。。。
答案不要抄错。。。
草稿一定要打清楚,划出专门的一块来做一道题。假如可以直接排列组合就不要DP,假如可以DP就不要手膜记忆化搜索树。
写DP表格画大画清楚。
多算几遍。。。而且CSP搞成选择题了,除非方法错,应该对不上选项就知道错了。
手膜题至少膜2遍。能看出程序在干什么最好,但是最好留下最后的时间略微手膜。
注意细节。比如某年的结尾逗号,某年堆排数组下标:ch[20]={‘q’,‘S’,‘O’,‘R’,‘T’…},那个’q’要了就死了。
尽量先看懂算法。想必CCF也不会搞出什么高端的东西。然后选择题对细节要求降低,实在不行联系上下文就好了。
今天2019.10.17,后天初赛。希望利用好明天一天,后天利用好早上两个小时,稳定发挥。
加油。
upd.
今天2019.10.18,明天就是初赛了。经过前面几个星期的系统复习,我觉得稳过初赛一点问题都没有。现在需要做的就是调整好状态,明天考试保持清醒和谨慎,把控好时间,留足时间检查。不要犯低级错误。
加油!
go to top