cin所需时间是scanf所需时间的3倍
spfa算法:
求解负边权的单源最短路算法,如果在正权图上应使用效率更高的Dijkstra算法。
我们约定加权有向图G不存在负权回路,即最短路径一定存在。用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止
定理:只要最短路径存在,上述SPFA算法必定能求出最小值。证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。
如果
判断是否存在负权回路,定义数组记录该点遍历过几次,如果超过n,即存在负权回路,原理:存在负环的节点会重复入队(因为最短路在不断变小)
void spfa()
{
queueque;
for(int i = 1; i <= n; i++)
dis[i] = INF;//初值记得赋无穷,而不是G数组中的值!
dis[1] = 0;
vis[1] = true;
que.push(1);
while(!que.empty())
{
int now = que.front();
que.pop();
vis[now] = false;//记得还原!!
for(int i = 1; i <= n; i++)
{
if(dis[now]+G[now][i]<dis[i])
{
dis[i] = dis[now]+G[now][i];
if(!vis[i])
{
vis[i] = true;
que.push(i);
}
}
}
}
printf("%d\n", dis[n]);
}
Dijkstra算法。
若给定的图存在负权边,类似Dijkstra算法等算法不可用
建立标记数组,
建立起始数组vis,要求点到个点距离
遍历vis找到最近且未标记点
标记该点
以该点为放缩点,找未标记的点,求最短距离
void dj()
{
memset(flag,0,sizeof(flag));
for (int i=1; i<=n; i++)
d[i]=tu[1][i];
flag[1]=1;
for(int i=1; i<=n-1; i++)
{
int x,u;
x=INF;
for(int j=1; j<=n; j++)
{
if(!flag[j]&&d[j]<x)
{
x=d[j];
u=j;
}
}
flag[u]=1;
for(int j=1; j<=n; j++)
{
if(d[j]>d[u]+tu[u][j]&&!flag[j])
d[j]=d[u]+tu[u][j];
}
}
cout<<d[n]<<endl;
}
manacher算法:
确定回文中心,以及确定回文半径
int MANACHER(char st,int len)
{
int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值,po当前回文中心
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2po-i]);//在Len[j]和mx-i中取个小
//回文半径大于i值
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(st[i-Len[i]]==st[i+Len[i]])
Len[i]++;//更新i的回文半径
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
//更新当前最大回文半径
}
ans=max(ans,Len[i]);
}
return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
最小表示法(最大表示法)
最小表示法是求与某个字符串循环同构的所有字符串中,字典序最小的串是哪个。
设i、j是两个“怀疑是最小的位置”,比如说如果你比较到了jdrakioi的两个i,你目前还不知道从哪个i开始的字符串是最小的。
设k表示,从i往后数和从j往后数,有多少是相同的。
开始时先设i=0,j=1,k=0。
每次都对i+k、j+k进行一次比较。
比较完i+k和j+k,如果两个字符相等,那么显然k++。
如果不相等,那么哪边比较大,哪边就肯定不是最小的了,同时把k重置为0。
如果出现了i、j重合的情况,把j往后移动一位。
最后输出i、j较小的那个就好了。
树:空树也是树,节点数减一为边数
priority_queue <int,vector,less > p;
.
.
priority_queue <int,vector,greater > q;
less:14 12 10 8 6
greater:6 8 10 12 14
如果使用优先队列结构体必须有结构体类型cmp
priority_queue<node,vector,cmp1> q1;
struct node
{
int fir,sec;
}input;
struct cmp1
{
bool operator () (const node &x,const node &y) const
{
return x.fir<y.fir;
}
};//当一个node x的fir值小于另一个node y的fir值时,称x<y
q.size();//返回q里元素个数
q.empty();//返回q是否为空,空则返回1,否则返回0
q.push(k);//在q的末尾插入k
q.pop();//删掉q的第一个元素
q.top();//返回q的第一个元素
查找一个数在一个数组中的某一段区间出现过几次方法
1.建立二维victor,输入的数组元素为行坐标,该元素在数组中位置为列坐标,
2.当查询在L到R出现几次x时
使用lower_bound(a,a+n,L)查询在victor中第x行的元素值大于等于L的下标,再upper_bound(a,a+n,R)查询在victor中第x行的元素值大于R的下标,两数相减,即一个数在一个数组中的某一段区间出现过几次方法
1.std::ios::sync_with_stdio(false);
百度了一下,原来而cin,cout之所以效率低,是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段语句可以来打消iostream的输入输出缓存,可以节省许多时间,使效率与scanf与printf相差无几,还有应注意的是scanf与printf使用的头文件应是stdio.h而不是iostream。
1. std::ios::sync_with_stdio(false);
2. std::cin.tie(0);
Map<string,int>p
P[string]=i;
A=i;
例如:lin zhou song bin
0 1 2 3
即如果输入zhou即等于输入1
C++ STL中的binary_search(二分查找)
前提已经排序
1.头文件
#include
2.功能
在数组中查找某个元素是否存在
函数模板:binary_search(arr[],arr[]+size,index)
参数说明:arr[]:数组首地址
size:数组元素个数
index:需要查找的元素
.
while(next_permutation(str.begin(),str.end()))
cout<<str<<endl;
.全排列函数
.
费马小定理:a^(p-1)%p=1,且a与p互质;
数据如果给的是1e9之类的一定是规律题目
next数组可以求最小循环节长度,比如打表找出其中最小循环,len-next[len],len表的长度
判断一个数是否取余20,只判断最后一位即可
判断一个数是否取余30,判断各位数字之和取余是3否为0即可
判断一个数是否取余90,判断各位数字之和取余是9否为0即可
判断一个数是否取余40,只判断这个数最后两位取余4是否为0即可
判断一个数是否取余110,判断这个数偶数位数字之和与奇数位数字之和的差取余11是否为0即可
同时能被11,7,13整除的数的特征,这三个数的末三位与末三位以前的数字组成的数之差能被7,11,13整除
线性筛法是指
第一个循环第一个数与第二个数之间差的循环递增
第二个循环第一个数与第一个与第一个成倍数的数的倍数的递增循环
gcd(x,y)=gcd(x,y-x);
gcd优化:
若x,y均为偶数,则gcd(x,y)=2gcd(x/2,y/2);
若x为奇数,y为偶数gcd(x,y)=gcd(x,y/2);
若x为偶数,y为奇数gcd(x,y)=gcd(x/2,y);
若x,y均为奇数gcd(x,y)=gcd(x-y,y);
lcm(x,y)=xy/gcd(x,y);
扩展欧几里得算法:
ax+by=m,且m%gcd(a,b)=0;
不断对a,b进行gcd运算,直到b0,使x=1,y=0,m=gcd(a,b),此时a=gcd(a,b),b=0;
递归回去求的x,y
举例50 120
运算过程
a b x y gcd(a,b)
120 50 _ _ _
50 120 _ _ _
50 20 _ _ _
20 10 _ _ _
10 0 1 0 10
20 10 0 1 10
50 20 1 -2 10
120 50 -2 5 10
50 120 5 -1 10
求解二元一次方程ax+by=m的一组解
即先求ax+by=gcd(a,b),的一组解x0,y0
所以ax+by=m的一组解为x=m/gcd(a,b)x0
y=m/gcd(a,b)y0;
32=2(mod5)
逆元…
下列矩阵旋转180°
3 4 5 5 6 7 7 6 5
4 5 6 4 5 6 6 5 4
5 6 7 3 4 5 5 4 3
所以对应位置相加得
10 10 10
10 10 10
10 10 10
可得
矩阵所有元素求和,若矩阵元素之间有一定规律可先将矩阵旋转180°
判断旋转后矩阵与原矩阵对应位置相加求的另一个矩阵,若另一个矩阵所有元素相等则该矩阵元素求和等于
矩阵首元素加矩阵末元素的和乘以矩阵规模再除以2
中国剩余定理
三三取余二,五五取余三,七七取余二,求最小的此数
xx取余a,yy取余b,zz取余c,
dxz+exy+fyz=k,dxz%y=0,exy%z=0,fyz%x=0;
所以所求数为k%xyz;
斐波那契额数列
|f0 n=0
F(n)=|f1 n=1
|af(n-1)+bf(n-2)
设k,m;
k=(a+(a2+4*b)(1/2))/2,m=(a-(a2+4*b)(1/2))/2,
F(n)=((kn)*(f(1)-m*f(0))-(mn)(f(1)-kf(0)))/(k-m)
给定一个数num.和一个数组a[],在空间复杂度为O(1),时间复杂度为O(n)下,使小于num的在数组左边,大于num的再数组右边
等于的放中间
建立两个指针,一个记录从[0,x]小于num在数组中的范围,一个记录从[0,y]小于等于num在数组中的范围,如果遇到相等于num
的值,此值于y+1交换并y++,如果遇到小于num的值,此值于x+1,交换并x++,然后此值于y+1交换并y++,
KMP算法中next数组的意义是该位置之前的子序列的前缀等于后缀的长度
给定一个串abcabc问最少添加几个字符构成的新串含两个原子串abcabcabc
求法令x=len-next[len],取最后x位添加到串最后即可
看了看别人的题解,都是枚举小于n的完全平方数输出,代码极为简单,因此本人不再给出代码。
给定以上两个树a,b;
如何证明树b出现在树a里面
将树a,树B 先序遍历,再kmp求解
BFPRT…不学
建立一个窗口,然后逐一从右边进入,如果比右边小直接进入,否则将右边的一项弹出,再判断直到符合为止,若全不符合,则全部清除,运算时注意窗口大小
建立两个以上的窗口max,min窗口,如果max-min<=num且范围从L到R,则L到R全部符合,
故从0开始运算L指针不动,R指针向右直到不成立为止,然后sum+=R-L;下来弹出0位置的相关信息,从1位置继续以上运算,运算时L和R指针不回退
单调栈:
求一个数左边最近比他大的,以及右边最近比他大的
建立一个从大到小的栈,如果为空直接进栈,如果比栈顶大直接进栈,如果比栈顶小,栈顶弹出元素,弹出的元素右边最近比他大的就是现在未入栈的元素,左边最近比他的就是弹出元素后的栈顶元素