【笔记+模板】图论 持续更新中

来源:互联网 发布:android 5.1 数据开关 编辑:程序博客网 时间:2024/06/08 01:55

      • 模板参见
    • 存图
    • 二分图判定
    • 最短路
      • 无权图
      • 有权图
        • Floyd 多源最短路
        • SPFA优化的BF
        • Dijkstras Algorithm
    • 最小生成树
      • kruskal算法
      • prim
      • 朴素prim堆优化prim和kruskal的算法效率实测
    • 差分约束

模板参见

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>=k1b>=k2b>=k3.....

的不等关系时,b的最小值为max{k},所以求的是最长路
将不等关系整理为下式,建图做最长路

d[v]>=d[u]+ < u,v >

求最大值时,

b<=k1b<=k2b<=k3.....

答案是min{k},求最短路

原创粉丝点击