模板参见
http://blog.csdn.net/loi_lxt/article/details/78249982
存图
//vector存图
vector<int> tu[N],cost[N];
void build(int f,int t,int v){
tu[f].push_back(t);
cost[f].push_back(v);
return ;
}
//邻接表存图
struct edge{
int f,t,v;
}e[M];
int next[M],first[N],tot;
memset(first,-1,sizeof(first));
void build(int f,int t,int v){
e[++tot]=(edge){f,t,v};
next[tot]=first[f];
first[f]=tot;
return ;
}
二分图判定
搜索,二分图染色
最短路
1.无权图
无权图:权值相等的图
bfs,dis=层数*权值;
2.有权图
Floyd 多源最短路
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
f[k][i][j]表示只经过1-k,i-j的最短距离;
SPFA(优化的BF)
可以处理负权,不能处理负环;
void spfa(int x){
dis[x]=0;
while(!Q.empty()) Q.pop();
Q.push(x);
inq[x]=1;
while(!Q.empty()){
int u=Q.front();
Q.pop();
inq[u]=0;
for(int i=first[u];i=-1;i=next[i]){
int v=e[i].t;
if(dis[v]>dis[u]+e[i].v){
dis[v]=dis[u]+e[i].v;
if(!inq[v]) Q.push(v),inq[v]=1;
}
}
}
return ;
}
玄学优化:
1.改stl里的队列为栈,栈快一丢丢;
2.双端队列优化,对于要放入队列的v,将其的dis与队首的元素s的dis比较,
dis[v]大于dis[s] v放队尾;
dis[v]小于dis[s] v放队首;
stl里的双端队列慢且优化效果宣勋,慎重优化;
3.spfa_cy %%%%cy学长
Dijkstra’s Algorithm
基于贪心思想的最短路算法,不能处理负权;
struct node{
int x;int dis;
};
bool operator < (node a,node b){
return a.dis>b.dis;
}
priority_queue<node> Q;
void dij(int x){
dis[x]=0;
while(!Q.empty()) Q.pop();
Q.push((node){x,0});
while(!Q.empty()){
node u=Q.top();Q.pop();
if(used[u.x]) continue;
used[u.x]=1;
for(int i=0;i<tu[u.x].size();i++){
int v=tu[u.x][i];
if(dis[v]>dis[u.x]+cost[u.x][i]){
dis[v]=dis[u.x]+cost[u.x][i];
Q.push((node){v,dis[v]});
}
}
}
return ;
}
dij中,在元素x出堆的瞬间,x的最短路已经确定,所以可以得到终点最短路后直接返回;
dij的贪心证明
从起点到一个点的最短路径一定会经过至少一个“中转点”(把起点也看作一个中转点)
在求某点的最短路之前,它的中转点的最短路一定被确定了
堆中存的是待确定最短路的点即它们的某些状态,
取出堆中dis最小的点x,它的中转点的dis小于x的dis,所以它的中转点一定早于x被拿出堆;
那么x的中转点最短路已确定,x的dis是由确定最短路的点(包括中转点)更新来的,所以x的dis就是x的最短路。
证毕;
最小生成树
1.kruskal算法
主要运用了排序和并查集的思想;
将所有的边排序,按照权值从小到大依次尝试是否加入最小生成树中,
同时用并查集维护节点之间的联通关系
时间复杂度主要在 排序nlogn,因此不适用于稠密图
2.prim
百度百科的prim讲解超级棒
https://baike.baidu.com/item/Prim/10242166?fr=aladdin
算法流程(引自百度百科)
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边< u , v >,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将 < u, v > 边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
优化
prim算法在选取当前权值最小的边时,朴素算法需要枚举一遍,类似于迪杰斯特拉算法,我们可以用一个堆(优先队列)来将寻找的时间复杂度由O(n)降到O(logn)
3.朴素prim,堆优化prim和kruskal的算法效率实测
http://blog.csdn.net/gykimo/article/details/8538275
差分约束
详见
http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html
差分约束系统就是将一些系数为-1,1的不等式关系,转化为图上最长最短路问题,利用一系列最短路算法(或变形)求解的一类问题
需要注意的是
求最小值时,即给定一系列形如
b>=k1
b>=k2
b>=k3
.....
的不等关系时,b的最小值为max{k},所以求的是最长路
将不等关系整理为下式,建图做最长路
d[v]>=d[u]+ < u,v >
求最大值时,
b<=k1
b<=k2
b<=k3
.....
答案是min{k},求最短路