随笔分类 - 解题报告
一些OJ上题目的解题报告,写给自己,总结经验
摘要:一道单源最短路的入门题,用于练习用C++写SPFA的模版,将边用邻接表建好,用SPFA求出最短路即可。#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<queue>using namespace std;const int MAX_EDGE = 2010 , MAX_VERTEX = 1010 , INF =0x3f3f3f3f;typedef class node{ public: int val , vertex; clas
阅读全文
摘要:一道很有趣的题目,主要是考思维,题目大概意思是有一群人需要过河,每个人过河都需要一定的时间,有一艘船可以容纳两个人,但是两个人上船过河,所花时间为两人中的较大者,问所有人过河的最短时间。 容易想到一种方案:花费时间最少的人和花费时间最多的人一起,然后时间最少的人回来。因为时间少的人会被时间多的人“拖累”,既然时间多的人不得不花这么长的时间过河,并且船还得回来,所以不妨让时间最少的人和他一起,由时间最少的人开船回来,这样额外时间(指一个人开船回来花的时间)会尽量少。 如果每次都这样做,总时间为 ( n - 2 ) * cost[ 0 ] + cost[ 1 ] + cost[ 2 ] +...
阅读全文
摘要:最大流的入门题,完全不需要建模,直接把1作为源点,m作为汇点用EK算法跑一遍即可。 这次第一次用C++写,EK使用的是网上找的模版,思想也许没有完全理解,待理解之后再写一篇EK的学习笔记吧,先暂时用着模版。#include<iostream>#include<stdio.h>#include<queue>#include<cstring>using namespace std;const int MAX_DIT_NUM = 210 , MAX_INTERSECTION_NUM = 210 , INF = 0x7fffffff;int cap[ M
阅读全文
摘要:一道略水的题,用树状数组求逆序数,放在这里当作模板,方便以后重用。 当时把树状数组的记录求和的数组开小了,一直WA,太粗心。#include<stdio.h>#include<string.h>#include<stdlib.h>#define MAX_LEN 160#define MAX_DNA 110#define MAX_RECRD 200struct node{ int val; char str[MAX_LEN];}dna[MAX_DNA];int getValue(char *,int),get_sum(int),cmp(const void *
阅读全文
摘要:这是一道在无向图中求割点的题,还是利用了tarjan。而割点需要满足的条件是:若u为根节点,u至少有两个子节点;若u不是根节点,那么它至少有一个子女w,从w出发不可能到达u的祖先,即low[w]>=dfn[u]。(Low[w]为w或w的子树中能通过非父子边追溯到的最早的节点,dfn[u]为u的深搜被遍历到的顺序。) 把它放在这里可以当作模板来参考,用数组map存储图,recrd标记是否满足low[w]>=dfn[u],sonTree记录子节点的个数。#include<stdio.h>#include<string.h>#include<stdbool.
阅读全文
摘要:这是一道Tarjan算法求强连通分量的题。方法是将强连通分量缩为一个点集,找出出度为0的点(或点集),值得注意的是,答案是存在于1个点集之内,如果存在两个点集合,那么它们就是互相可达的,于是又属于一个强连通分量,所以也应该缩为一个点集。 由于数据量比较大,内存有限制,所以使用邻接表而不是邻接矩阵。由于以前觉得邻接矩阵比邻接表好写,所以对于邻接表只是略微的了解了一下,这次是第一次写邻接表,所以走了许多弯路:在节点的遍历上,我专门写了一个函数来判断两个节点之间有没有路,由于tarjan函数和求出度的时候都会用到,所以大量的函数调用造成了TLE;对于head数组,开始开的是一维,所以又写了一个...
阅读全文
摘要:这是一道tarjan算法(求一个有向图的强连通分支)的应用,这道题主要用于熟悉tarjan算法。题意是说有一些学校,每个学校都有一个list,比如a学校的list有b,c,那么a拿到一种软件后会分享给b、c(b拿到不会给a,如果a不在b的list上的话,c也是这样),所以就建成了一个有向图。问(1)至少给几个学校能够确保所有学校都能够拿到软件;(2)至少在某些list上添多少学校,可以确保任意给一个学校软件,所有学校都能拿到。 思路大致是,利用tarjan算法得到有向图打强连通分支,把它们全部看作一个节点,那么最后显然应该给所有出度为0的节点软件,第一个问题得以解决,同时,这样做之后,图...
阅读全文
摘要:这是一道差分约束系统的题目。所给的约束条件是对于一个序列的子序列来说,子序列的和大于或小于一个数,问是否存在一个序列,使得所有约束条件全部成立。 我采用用数列中类似于数列求和的方式,比如序列a1,a2,a3....令s1=a1,s2=a1+a2,s3=a1+a2+a3...为了方便,我还引入了s0=0,这样就可以根据题目的条件列出不等式,再把不等式转化为求最短路的问题。开始我想当然地认为si>=si-1(i和i-1为下标),加了这个条件之后当然是错的,后来发现了这个问题,将其去掉,但是却忽略了一点:这道题其实是让我判断是否存在负环。因为图可能不连通,所以应该每个点都要作为一次起点,用SP
阅读全文
摘要:这是一道差分约束系统的题,刚开始学,还有些地方不是很清楚,靠这道题弄清了一些问题。这道题是是说有一队奶牛要吃饭,有些奶牛互相讨厌,必须至少相隔一定的距离;有些奶牛互相吸引,距离不能大于一个定值。另外,这些奶牛可以处在同一个位置。求这队奶牛最远的长度。 拿题目的数据来说(1,3,10)、(2,4,20)、(2,3,3),很容易得出不等式:x3-x1<=10;x4-x2<=20;x3-x2>=3。并且,由于奶牛顺序不能改变,所以Xi>=Xi-1(i-1是下标)。我们可以把不等式变形为:x2<=x1+5;x3<=x1+10;x2<=x3-20。从而转化为求最
阅读全文
摘要:这是一道简单的KMP求next数组的题,几乎不用动脑筋,可以用来练练手,直接把两个字符串连接在一起求next就行了,唯一要注意的就是长度不能大于原来任一字符串的长度,如果长度大于了,要选择len1和len2中较小的一个输出。#include<stdio.h>#include<string.h>#define MAX_STR 50005char str1[MAX_STR*2],str2[MAX_STR]; int next[MAX_STR*2];void get_next(int,int);int main(){ char oupt[MAX_STR]; while(sca
阅读全文
摘要:这道题是考察对next数组性质的应用。题意相当于是求一个长字符串中循环节的个数。 由于我们知道next数组中存的是一个位置(假设next[j]的值为k,对应的字符串为M,如果k>0,那么M[0....k-1]和M[j-k.....j-1]是相同的,并且0...k-1这个序列一定是最长的),比如a b c a b c d(next值:-1 0 0 0 1 2 3 ),由next[6]=3可知,M[0..2]=M[3..6],这就找到了循环节,于是我们思考从next数组作为切入点,来找到一种方法来求得循环节的个数。 看看next数组的一个性质:next始终是从-1开始增加(在变为0之前)。.
阅读全文
摘要:一道KMP算法的基础题,用于熟悉KMP算法和求next数组,关于KMP算法,为自己写了一篇总结。 这道题要注意的是,模式串可以重叠,比如给出ACA与ACACACA,应该出现了3次,这里只需要在KMP的函数里稍微改动一下即可:如果模式串的位置等于了模式串的长度,说明模式串已经被完整地匹配了,并且,此时模式串的位置由于等于了长度,所以这里是并没有字符的,但next值会存在(详情见求next数组的值的函数),此时只需要假设它们不匹配,转到此时的next值即可。#include<stdio.h>#include<string.h>#define MAX_WORD 10005#d
阅读全文
摘要:这是一道二维的树状数组。与一维不同之处在于更新和求和都多了一重循环:更新:void update(int a,int b,int val){ int i,j; for(i=b;i<=lmt;i+=lowbit(i)) { for(j=a;j<=lmt;j+=lowbit(j)) { c[i][j]+=val; } }}求和:int get_sum(int a,int b){ int i,j,sum=0; for(i=b;i>0;i-=lowbit(i)) { for(j=...
阅读全文
摘要:一道树状数组的应用题,和POJ 3067非常类似,这道题是求比自己强壮(e更大,s更小)的牛的数量。 将POJ 3067的思路应用到这道题上,如果先对e按照降序排列,每加入一只牛,当前已经加入树状数组的牛的s如果比这只牛小,那么那些牛就更强壮,所以同样是在树状数组里的求和问题。同样,对s的排序规则关系到e相同时的情况,由于s更小就更强壮,所以先把s小的加入,于是s就按照升序排列。再考虑e和s都相同的情况,假设有一些牛的e和s都相同,为cow1,cow2.....,cowK,那么更强壮的牛的数量x,满足cow1=cow2=....=cowK=x。所以遇到e和s都相同的状况时,只需要复制答案...
阅读全文
摘要:这是一道运用树状数组的题。大意是求直线的交点数,前提是交点只能有两条直线相交,不能出现三点交于一点的情况。需要注意的是就是排序的规则。 假设东海岸的城市坐标为x,西海岸的城市坐标为y,当有一条公路(x1,y1)时,再修一条公路(x2,y2)与(x1,y1)相交,显然,当(i)x2>x1且y2<y1或(ii)x2<x1且y2>y1时,两条公路才会有交点,由于此时x,y有两个变量,都在变化,不好思考,不妨将y按照降序排列。那么,当有一条公路(x1,y1)时,需要再修一条(x2,y2),由于此时y已经按照降序排列了,所以y2<=y1.当y2<y1时,如果此时x2&
阅读全文
摘要:这是一道树状数组的基本操作题,熟悉树状数组的更新操作和求和操作。题意是求对一个坐标 (X,Y),求 (x,y)的个数,其中x<=X,y<=Y。由于坐标按照y的升序给出(如果y相同,又按x的升序给出),所以这里只用得着x一个坐标,每得到一组坐标(a,b),就求x坐标小于等于a的星星个数,再将坐标为a的星星个数更新(加1)。 另外,为防止x=0在lowbit()中造成死循环,每次得到x坐标都应该加1.#include<stdio.h>#include<string.h>#define MAX_STAR 32010 int n,c[MAX_STAR],level[
阅读全文
摘要:这是一道树状数组的应用题,题意是要对一个序列按升序排序,每次交换相邻两个数,求交换次数。假设a,b,c相邻,如果a>b,a>c,那么a,b就应该先交换一次,a和c再交换一次。所以这道题就抽象为求逆序数,即对每个i,找到a[i]的左边比a[i]大的数的个数,求出总和。由于树状数组可以很方便地求出一个数左边比它小的数的个数,所以进一步转化为:对每个i,找到从a[i]开始,到最左边小于等于a[i]的数的个数,然后用i减去这个数,就得到a[i]左边比a[i]大的数的个数。 具体来说,就是维护一个数组(c[]),初始化所有值为0,对需要统计逆序数的序列,从左向右依次加入序列中的点,比如序列a
阅读全文
摘要:一道线段树的区间修改题,给区间染色,最后输出一个区间的颜色种数,由于颜色不会超过30种,所以可以用int型二进制的每一位表示一种颜色,比如颜色1就是000……0001,颜色3是000……100,那么一个区间内如果有两种颜色A和B,管辖这两个区间的大区间的颜色就为 A|B,最后根据查询到的数得出二进制中1的个数,从而判断出有几种颜色。 写这道题的时候,swap函数出现了一个错误: swap(int *a,int *b) int *c if(*a>*b) *c=*a; *a=*b; *b=*a; 这暴露出我的C语言基础还不扎实,开始声明*c的时候没有赋值,后来又要对...
阅读全文
摘要:一道线段树的区间修改题,和POJ3468非常类似,都是利用lazy-tag来加快速度,lazy-tag在poj 3468的解题报告中有说明。不同之处在于这里是更改stick的类型,所以对tag直接赋值就行了。另外,初始的stick为1,所以在建树的时候需要注意。#include<stdio.h>#include<string.h>#define MAX_HOOK 100005struct node{ int left; int right; int sum; int tag;}stick[4*MAX_HOOK];void build(int,int,int);voi..
阅读全文
摘要:一道线段树的区间修改题,以前写的线段树都是单点修改,这里用到了区间修改。我开始觉得单点修改和区间修改差不多,把区间内每个点进行一次单点修改就是区间修改了,这样虽是可行,但是时间复杂度却大大增加,想想看,一个点一个点地修改,这和暴力有什么区别呢。 在网上找了好多资料,终于弄明白了用lazy-tag(lazy标记)进行区间修改。我的理解是这样的,为了减小修改次数,先将要进行的修改存起来,当访问到子节点的时候,如果发现父节点带有标记,就顺便将子节点更新,就像搭顺风车一样。在这道题中,每次可能要对一个区间加上一个值,比如全集为[1,7],现在要对区间[1,4]进行操作,例如要将[1,4]每个值都...
阅读全文
浙公网安备 33010602011771号