概述
博客草稿箱里有好多要填的坑 考前注意看看
这两天被规律题虐得不行
因为我都是那种,通过题意看性质的做法。。
然鹅性质可能过于复杂,看不出来
所以就要打表。。。
####手打+程序打结合,手打验证正确性,程序打表是为了打的更大一点,因为很多时候手打表打的小了观察不出规律
就是,真的去把表格列出来,找上下行,不同列的关系
还有就是找规律真有可能找错,
##先打好暴力,然后对拍什么的网盘里有对拍程序
还有 写代码的时候一定要动脑子!动脑子!动脑子! 特别是那种
x x x x x x x
x x x x x x x
x x x x x x x
x x x x x x x
这样的
比如说有x次操作,每次操作是
a
i
=
a
i
+
a
i
m
o
d
n
+
1
a_i = a_i + a_{i mod n + 1}
ai=ai+aimodn+1求第x次操作后位置y的数是多少
打出表来。。。
1 2 3 4 5
3 5 7 9 6
8 12 16 15 9
20 28 31 24 17
然后发现每个数都是他上面两个数的和
然后看看位置y的数是怎么加起来的,会发现这个东西和组合数用关系
###关于智商题…
这两天被智商题虐的不行。。。
其实我的思维方式有问题…我总是不去模拟那个过程,而是非常“混沌”地想这是什么,但是混沌的想法怎么能出来清晰的解法呢…就得按部就班地进行那个过程,看看这个过程会发生什么,特别注意若有n个东西移动,那么把范围小一点,只考虑一两个,然后推出来某结论时也要注意不能丢了题意给的过程,所以说逻辑是战胜混沌的重要方法啊…
比如说洛谷上的独木桥那题
如果我画画图,模拟人怎么走,走完后又是什么状态,结合每个人速度一样就能找到解法
但是我最不好的地方就是找到了一个性质后,非要热衷于找反例
找反例是好的,但是很多时候乱构造出的反例可能不存在,但是有时候又容易忽视掉不存在那种情况的可能
导致题做不出来
而且我总是“混沌”地想,数据这么乱,怎么可能都满足这个性质呢
但是这是题啊。。。题一定有他的做法的,你总要依据题意,从中挖掘出什么重要性质,而且oi不需要证明
关键是找反例这里,千万不要乱构造反例了,以自己的角度很难知道这个反例哪里不合理,只要证明那个性质具有普遍性就可以了
所以说性质别乱找反例,但贪心可以找反例,因为容易贪错
####“!” 运算符的优先级很低,所以注意判断奇偶数时要在括号里%
!(a%2) 或者直接if(a%2)判断是奇数吧
###同时也要注意,即使考场上没有部分分的数据,写完部分分后自己造一组那样的数据测一下,防止出现低级错误
注意 双向边定义数组大小时必须写乘2
这个有时候忘了就很难受,就写e[MAXN*2],反正一定要记着数组开两倍(因为大多数时候N,M同级,所以就写MAXN了)
###多组数据数组记得清0,多次操作记得清0,比如建新图,基本都要清,包括邻接表总数totnew,再比如说dfs的vis数组,还有每次vis[1] = 1, ans = 0 清空上一次,最小值初始为1<<30最大值设为0,这个别忽略,有一题多组询问,要求我选任一个点,每次跑最短的,然后我写了n^3暴力做法,但是每次询问前我都需要把答案设为1<<30 因为我每次询问就要出一个答案,而上一次的询问和这一次的没有关系,看来一个询问的答案最好开在循环里 实在要开全局的一定注意每次更新(不同的询问)时答案必须先设为0或1<<30 具体看寻宝游戏写的暴力
总之就是多写memset
模的时候要注意 举个例子 比如说我要绕环走,那么我的位置就是(y+k)% n y起始位置(把y+n当做一个整体,从1号点走到y再走到n),k走的距离,n个数
那么有个问题 如果说y+k == n的倍数 那么%后变为0,但是我们应该走到n的
可以设a[0] = a[n]
也可以只考虑k,把k单独%n 再加上y 特判当y+k%n > n 时减去n
也就是说%n次之后正好回到了y,再考虑剩下怎么走
然而还是容易错,出几组极特殊的例子测测就行
或者更简单的办法是从0开始计数 当y+k = n时模一下正好回到开头 而n-1是结尾0是开头
字符串从0到n-1
状压dp从0到 (1<<n)-1
如果说一个题,调了半天,然后勉强过了样例,把一个算法修修补补,那很有可能写错了,最好重新做下这道题 必须要重看一遍题面,把整个过程把握住,不要想到哪写到哪,写的程序要有条理。。。我17年时间复杂度本来该拿100分,但是拿了70,因为我调过了大样例,但估计只是勉强过得,我并没有想用两个栈,我只是一边读题一边写
###最好模块式编程,先写函数名和注释,说明函数是干什么的,然后先别写函数 把主框架写出来
文件一定不要写错,有时候眼看不出来,抄下来一个个对,或者分时间段多看几次
数组开大点,如果说只用01两个变量开到3 lca倍增20次就行开到25
输出注意写%lld !!!
队列开两倍大,万一越界了呢
而且做单调队列题的时候注意若有两个以上的单调队列最好开两个队列数组并且每次都把l,r设为 l = 1 r = 0
数组开大点 比如说开了个dp数组 里面有用0/1表示什么什么 那么得开成dp[3][MAXN]
又被多组询问坑了。。。
多组询问时 若成功则flg = true;
注意这个flg要写成局部变量并且每次都赋false
一定要明确是否有向无向图啊啊啊啊啊啊
读题要认真!
FJ在第i号隔间上张贴了一个“下一个隔间”Next_i(1<=Next_i<=N),告诉奶牛要去的下一个隔间;
这指的是有向边
而有些图需要建无向边,比如奶酪那题,注意题意。
博弈找规律一般都和二进制有关,什么异或啊,或者把每一个状态的二进制转换成十进制之后能发现这个十进制数有什么联系什么的
#开long long 一定记着要开long long 答案 数据超了int一定要有意识地开long long
真实的故事 我是如何wa的
int maxx = 0;
for(int i=l; i<=r; i++) {
maxx = max(maxx, a[i]);
}
然而一不小心写成了
int maxx;
for(int i=l; i<=r; i++) {
maxx = max(maxx, a[i]);
}
wa
还有更可怕的。。。
void calc(int p) {
...
}
long long p;
calc(p)
哇 我把longlong填到int参数里了!!!
应该是
void calc(long long p) {
...
}
long long p;
calc(p)
还有 返回值应该是long long 的函数 一不小心定义成返回int的函数了。。。
long long dfs() {
}
printf("%lld", dfs());
int dfs() {
}
printf("%d", dfs());
如何写搜索,搜索和DP内核是一样的 需要记录状态,而不同的分支就是不同的决策
先确定阶段 比如 T40984 Chino的成绩 这题的搜索
阶段是到了第几天,决策是这一天怎么做,我可以继续前几天的方式 或者用另一种方式
##静态查错真的很重要
long long double 的位运算可能会出锅 所以注意这两种类型就老老实实用/2和%2代替位运算吧
注意运用洛谷“推荐的相关题目”进行网形做题
先看数据范围,猜复杂度,然后猜算法
##变量名不要起的长得太像 真的很容易看混乱
一定要长个心眼,别被出题人坑了。。。比如说有个bfs找联通块,要求从第一行开始从左往右然后第二行从左往右,求每个联通块在这种起点下扩展的最大深度 这个最大深度要放在每个元素出队的时候更新,而不是元素入队时候更新,因为可能联通块大小为1,不发生任何入队,而因为最大深度每次重置为0 导致不能更新大小为1的联通块
#对于字符串DP 一般都会给出字符串长度,此时要用字符数组存串,并且写cin >> a+1;这种形式 若不给出字符串长度 用 strlen(a+1)求长度。 为什么不用scanf呢。。因为scanf会读换行符。。。而且字符串DP一般来说复杂度都较大,而输入一般都是几千的数据,所以不在乎cin还是scanf了。。
不仅仅是如此 任何从下标0开始的DP都有可能访问负下标,所以想办法让下标从1开始,而且从0开始不只有负下标这一种可能出错。。。很多时候你想不到,反正从1开始准没错
啊啊啊啊啊我字符串DP访问到-1了好难受啊
所以静态查错太重要了,不仅要思考高级错误,也要思考低级错误,若能大致地证明正确性,那么不妨检查一波坑人的地方,写完一道题后,花个5分钟找找坑点是很好的!顺便也能放松一下,换换思路。可以想想如果自己是出题人面对这么一份代码该怎么卡掉
在静态查错的过程中切不能急躁,要想清楚自己在干什么,自己的算法什么地方可能会有问题,包括高级(想法上的)和低级错误,比如没清空,longlong,负下标, x%n = 0但是想让x为n这样的
特别是那种“水题”,更有可能出现低级错误,因为有算法的题的处理一般是难想难写,相对来说坑人的点不会很多
建议先花时间只看一遍所有的题 不写代码 把思路写在txt上,因为用语言规范自己的思路会自然地发现错误,而一旦开始写代码了就会头晕脑胀。。。不如先把做法想好,但是要先写好输入。。。
另外 对于打开思路
没事打打草,有用的性质都记录下来,零散的思路也画下来
加强或者放宽题目的条件或约束,得到特殊的模型。思考特殊模型的解法并推广到原问题
是否存在一些隐藏的性质?要从边界或特殊情况考虑,猜测性质。
换个思路?正难则反,统计求贡献?像二分一样求解转判定?和组合数是不是有点关系?一般来说Noip题步骤不会特别多,想不出来可能是方向问题
其实最有用的是
根据范围猜正解复杂度,然后根据复杂度猜算法,这样能提供一个思考的方向,一道题要是n^2那自然就是两两之间处理一下什么的。
实在没有思路的时候,把自己学过的算法,知识点全过一遍,看看什么能用上,很多题的贡献可能就是组合数的某一项,或者是欧拉函数什么的
或者直接把自己学过的算法都来一遍,这个过程其实很快,不到5分钟吧,看看有什么算法能用的,或者说某个算法的某个思想我能借用(因为题可能就是从某个算法得到了启发才出出来的)
话说 签到题可以直接想正解,想完正解要是不放心再打暴力对拍,因为说实话签到题没必要对数据分开写算法了,而且一些水题的暴力做法可能会影响到想正解的思路(可能暴力做法和正解没啥关系。。。毕竟是签到题不会出的那么猛)
一定要算复杂度,要是达到了1e8级别就要卡常,比如stl改成手打的,scanf读入什么的
不会做题了注意找性质,有时候看似没什么关系的性质恰恰是突破点,具体来说就是手玩一下样例,看看量之间有什么关系
今天一道超简单的暴力没打出来。。。题目是给定一个括号序列,求最长合法匹配子序列
暴力n^2可过,然而我没想出来
事实上只要当成括号匹配用栈做就好了,因为所谓的子序列其实没啥影响
我应该先考虑简单的情况开始,再想复杂的
()
这样的情况显然只需要按平常的匹配方式就好了,然后
( ) )
这样的话最后一个右括号直接舍弃掉就好了,然后一直这么做,不能匹配的直接丢掉
注意别老给自己设麻烦,前几天做一个排列组合题怎么也想不到重复的方案是什么形式,我又犯了由一般到特殊的错误方法论,实际上我自己手玩个小的情况就能很好的看出什么样的形式是重复的(比如说让一些人围成一圈坐着,某些人不能坐一块,求方案数,我可以先玩一下只有3个人的时候的情况,并且看看什么样子是重复的,很容易想到重复的方案就是那些旋转以后等效的方案,但是如果我不玩一下,这个问题就会变的很难思考,不如搞个小样例然后猜想一下。。。)
多组数据记得造含有多组数据的小样例,不然可能会被坑。。。
能打表的数学题一定要打表,对于我来说找规律是最好的做法。。。
好好读题好好读题,别再犯什么考试都犯不好好读题的毛病
DP方程最好写在纸上,事实上许多思考过程都放脑子里很容易乱,用纸记录下是很好的!
注意找性质,探索探索,比如说巴什博奕,考虑局面有m个,m+1个,m+2个等情况就能看出来解法
还有你想求出某个特定的子图,你应该想,满足这样要求的边有什么性质,而不是去一直想什么复杂的染色什么
的
没思路的时候可以乱找一些性质,探索探索一些奇怪的性质(即使看起来这个性质目前并不能拿来做题)
比如判定一个区间内是否存在一个数能整除所有数,只需要知道这个区间的最小值和区间最大公约数是否相同即可
多组数据一定要用有多组数据的样例测试一下
注意从多个角度找性质,很多时候第一眼看出来的性质可能并不是正解,而且当年发现这个性质几乎没什么做法的时候可能是思路歪了,从头开始仔细发掘一些东西
typedef long long ll
const long long INF = 1ll <<61;
这里inf的类型别开成int,还有注意左移运算的时候数字“1”的类型默认是int,要转换成long long 才能正确的得到2^61
注意dij,q.push时,括号里元素的顺序,和结构体里面定义的要一样
对于简单的题可以直接想正解,但是注意先把题都看完再说,反正最简单的那道题早晚都得写正解。。。但是得确保自己先做的是最简单的
字符串题虽然数组一般不用开很大(比如记录26个字母什么的),但还是建议开大一点,内存够用的话,开大点会自然解决一些小问题
注意bool型的dfs一定要在最后加return false
用if去判断
bool dfs(int x) {
if(x == n+1) {
if(judge()) return true;
return false;
}
for(int i=1; i<=n; i++) {
num[x] = i;
if(dfs(x+1)) return true;
}
return false;
}
注意根据复杂度猜算法 1000000 O(n log n)可以过,但是要思考有没有On做法,比如说双指针,单调队列什么的
注意写floyed初始化数组为无穷大
异或的性质:
1、交换律
2、结合律(即(a ^ b ) ^ c == a ^ ( b ^ c))
3、对于任何数x,都有x ^ x=0,x ^ 0=x
4、自反性 A XOR B XOR B = A xor 0 = A
longlong输出用cout吧。。。
任意两点只有一条最短路的图是一棵树
线段树易错点:
应该写成左右子节点:query(w << 1 | 1, mid+1, r, x, y);,然而w<<1|1写成了w。。。
树剖建树应该在两次dfs之后,并且用rnk数组映射dfn序为编号
路径查询/修改的时候比较的应该是两个点的dep[top[x]],比较的是深度!
单点,建树的时候注意
if(l == r) {
tr[w].sum = a_init[rnk[l]];
return;
}
这里是tr[w].sum不要写成l或者r,l,r是唯一对应的辅助变量,而w是指向节点的指针
注意加上return 0
平时多练练手速,树剖和平衡树什么的
注意最后删除你数据分治的限制(if什么的),万一多A一个点呢?
注意这个now应该设置为比最小值更要小的值,才能求出正确的最大值,这里因为输入中有许多0,所以最小值其实是0,now初始值得设成-1
int max_query(int w, int l, int r, int x, int y) {
if(x <= l && r <= y) {
return w;
}
ll now = -1, pos = 0, tmp = 0;
int le = w * 2, ri = w * 2 + 1, mid = (l + r) / 2;
if(x <= mid) {
tmp = max_query(le, l, mid, x, y);
if(now < tr[tmp].m) {
now = tr[tmp].m;
pos = tmp;
}
}
if(y > mid) {
tmp = max_query(ri, mid+1, r, x, y);
if(now < tr[tmp].m) {
now = tr[tmp].m;
pos = tmp;
}
}
return pos;
}
一个求最优解的题,要么是数学,要么是贪心,要么是dp或者建图
总之不能只想一个思路,求最优解的题多想想能不能DP,或者建个图跑个算法,只想贪心是不对的,虽然我每次第一反应都是想贪心,但是提高组一天至少一道DP,所以你可以多想想哪道题是DP(或者说是:今天的DP题去哪了?)
从前我都是照着算法标签做题,这导致我很多题看不出来是什么算法,所以要锻炼自己的能力,总结一个学过的做题方法表,不会的时候把表顺一遍,看看什么做法能用
学了知识要会用,不然就是没学!!!
自己造数据的时候,注意题目的特殊限制,不然可能程序本身没错,是自造数据有问题,然后会浪费大量时间debug
比如r - l <= 1e7这样的,显然不能随便造l和r
有些时候可能优化后复杂度也不能通过更多的点,这个时候还要不要打优化算法呢?
那么得仔细算算复杂度,不能笼统地算,得看题目中的特殊限制,有一次一个矩阵我以为N ^ 4枚举和N ^ 6枚举得到的分一样,所以不加矩阵前缀和,然而真正的复杂度是N ^ 2 * M ^ 2 而 N,M不是一个数量级,打优化是可以的!
写了错误的算法会浪费很多时间!
注意对于多组询问的题,造几组询问区间相同与不同的数据
还有种题是每次询问做一次DP,注意清空DP数组
有时候N ^ 3算法比N ^ 2 算法难写。。。有些DP递推形式(前i个中)比以xxx为结尾那种n^2形式要好写很多,也更好想
注意判断许多东西的时候,最好多开个布尔变量来做,比如判定叶子节点,就把布尔变量的赋值开在遍历邻接表里,这样若没有遍历邻接表就说明是叶子节点。如果靠某些性质来判断叶子结点,可能会出问题,比如说我认为叶子结点的dp数组值为0,但是如果题目中的边权值都是0呢?
有些答案的计算可能看起来很麻烦,手玩一组举个小例子就发现怎么求了(比如说怎么算贡献什么的)
注意使用快读
一定要三个题都看一遍从简单到难的开始做,并且注意看的时候预估最大值考虑开不开long long 并且记录下来
我很多时候会犯一个思路错误,我目前的算法有点小毛病,我直接弃了,过了半天还是得用这个算法,事实上我应该尽力去试着解决问题(甚至特判)要善于发散思维,灵活思考,抓住某个线索一定要顺着想下去,看起来这个想法乱糟糟的,或许理顺一下就是正解了呢?
注意搜索的写法:
if(x == t) {
flg = true;
ans = min(ans, now);
return;
}
if(vis[x]) return;
vis[x] = 1;
这样我们就可以多次到达终点并且更新答案,如果你把vis放到x == t上面则终点只会被走一次就返回了,这样答案就不会被更新了
有向图考虑和DAG是不是有关,同一个强联通分量是不是能缩起来
有向图做搜索的时候注意是否能“绕圈”,若能绕圈就不能判重了…但是可以考虑缩点,比如说我可以绕一圈使得答案变小之后回到某个点从另一条路再出去
检查自己的快读:输入0 9 90看是否输出正确即可
写快读之前 注意输入有没有负数!!!
dp[v] = min(dp[v], dp[x] + w)的if版本是:
if(dp[v] > dp[x] + w) dp[v] = dp[x] + w;
注意是大于时才更新
注意longlong类型的数组开到5000X5000就花费了180M内存,算内存的时候longlong要乘8
求大组合数的时候注意若
C
n
m
C_n^m
Cnm中n,m有大于模数p的,不能用逆元的方式了,最好用lucas定理
若预处理逆元,注意0的逆元也得求出来(求的组合数里面可能有n = m的)
考试中如果下发了大样例,最好先写有大样例的题的正解
关于对暴力算法的优化:可以考虑哪些量其实可以提前算出来(算贡献什么的),不需要像暴力算法那样不优美地枚举,现用先算总是慢的,提前算复杂度还是低了很多的
循环变量别再i里面套个i了QAQ
!!!更新暴力算法的时候注意数组也得更新,而且试试最大数据!
比如说想到了一个可以过1e6的算法,可是之前算法的数组只开到了1e5,这样会re啊!!!
注意如果想到了一个算法,复杂度不对,数据随机或许能卡过去,要不要写一定要多考虑下,因为数据真的可能专门构造过,而且这种算法往往和出题人设置的不符,更为难写,难以调试
全排列的时候注意最后计算的时候要用的是方案数组pla而不是原始数组a
我写错好多次了!!!
void dfs(int x) {
if(x == n + 1) {
ans = max(ans, calc());
return;
}
for(int i=1; i<=n; i++) {
if(!vis[i]) {
vis[i] = 1;
pla[++tot] = a[i];
dfs(x + 1);
vis[i] = 0;
tot--;
}
}
}
for(int i=1; i<=n; i++) {
sum += pla[i];
}
注意搜素之后的还原现场要做好,被修改的一切东西都要变回去,具体可以看看愤怒的小鸟那题的搜索
早上刚起来不清醒,应该先读题打暴力,进入状态再想正解
学会看透问题的本质,别乱上什么贪心乱搞算法
我乱搞题做得太多了现在看到一道题就觉得是乱搞,贪心一下什么的
其实多往某个知识点上面靠靠不就好了,根据题目性质想想这是个什么问题,有什么相关算法
你得想,这个题他要考察你什么(如果这个题不是乱搞的话),出题人是以什么算法为基础延伸出这个题的
别一上来就乱想做法。。。方向比深度更重要!
基本算法与使用相关问题:
前缀和 - 统计区间
二分答案转换为判定,比如减去二分值判定非负什么的,不符合二分的元素(一条边)删除掉看最后是否存在方案什么的,就是把不符合二分值得全部都删除掉
逆序对 中位数 二分 - 有序序列
区间贪心 - 按左/右端点排序,若按右端点递增排序,在枚举的时候,可知目前最小右端点
注意问题的转化,别太死板硬刚原始题意
贪心想不出来的时候多想想DP
一定要根据分类讨论写的程序造出分类讨论的数据!!!
如果一个算法是分类的,我可能是把第一类的复制过来改了改,这个时候注意,可能也把bug复制过来了,如果说找到了bug,也要注意复制过去的那部分有没有bug
快读注意不能读long long。。。
暴力算法拿不到该拿的分的时候注意想办法优化一下而不是只想正解
比如最短路删边问题,枚举删除是MNlogN的复杂度,有点慢,但是优化一下,只删除最短路上面的边就会变得很快,如果暴力复杂度不对,先想想怎么优化而不是直接换思路做
使用pre,last这样的表示“上一个”的变量时一定要注意特判“第一个”,比如pre设
1的逆元是1
如果用矩阵存图,一定要判这条边是否存在,即!gra[u][v]
你会发现二分的复杂度其实写成logn * n更好一点
别被一些题目之外的东西限制思想,分析题目性质,读懂题意实质
比如借教室这道题,我就一直在想差分差分,但是差分什么啊!!!就算我猜出用什么呢算法,但是我猜错了算法的使用顺序,就一直想用差分去解决二分答案的任务
正确的思路应当是看出答案单调性用二分,然后用差分去判定
一定要使用更优美的写法,思考目前对于算法的描述是不是不大优美,不大简洁,能不能用一些稍微复杂一点的处理方式,让最后统计答案的时候更简洁,不需要分类讨论,直接统计那种的,特别是简单题,其正解写法必然巧妙而不杂乱,而且更加抽象,把问题的几个元素,用程序保存的很抽象,很简洁,暴力算法是对的,但是暴力处理就不大好,可以多想想怎么写会让代码更加简洁和抽象,即使稍微用了点麻烦的算法
比如说xy坐标轴内一条长度为1,平行于x轴的线段,其坐标为(x,y)(x+1,y)
顺时针给出一个各边平行于坐标轴的多边形各点,我需要这个多边形上所有的横向线段,我应该如何保存?
我直接把格点保存下来吗?然后判断相邻两个x=0直线上的平行位置上是否都有两个点?不大好吧,如果出现一个凹形我的算法就错了
因为我这样保存的不是横线,而是间接保存了一些点,然后间接确定直线,这样不大好,不如定义一个保存规则,直接把横线保存在一个数组里面,表示x ~ x + 1上有一条横线,这样我去访问横线的时候也很方便,不需要两头都有一个点了
注意一些常用变量的重名问题
比如输入中给了个以s名字的变量,我在树剖的时候也会用到s这个名字。。。所以最好把输入的变量不要用名字s,设置为sss什么的…
为了不和std的命名空间重名记得变量名起的奇怪一点…
正难则反,问题要求一个东西,我们可以反过来想这个东西可以怎么怎么样
这是一种抽象的反向思维,比如说p1311选择客栈那题,问我有多少种住宿方案使得之间有一个符合的咖啡馆,枚举复杂度实在太高了,而不枚举是无法确定哪个咖啡馆是满足要求的,不妨反过来,枚举每个符合要求的咖啡馆,看他能对于几个方案
最后
以上就是苗条雨为你收集整理的【自用】整理(bugs,总结)的全部内容,希望文章能够帮你解决【自用】整理(bugs,总结)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复