最短路


dijkstra:单源点最短路,不能判断负权
比如n=3,邻接矩阵:


  0,3,4


  3,0,-2


  4,-2,  0


用dijkstra求得d[1,2]=3,事实上d[1,2]=2,就是通过了1-3-2使得路径减小。
int main()
{
   for(int i = 0; i < n; i++)
    d[i] = map[x][i];//d[i]表示从源点到各点的距离
    vis[x] = 1;//起始点已经加入集合
    d[x] = 0;
    for(int i = 0; i < n; i++)
    {
        min = max;
        for(int j = 0; j < n; j ++)
        {
            if(!vis[j]&&min < d[j] )
                
            {
               int  p = j;
               
                min = d[p];
            }
            vis[p] = 1;//找距离源点最小的点
            for(int j = 0; j < n; j++)
            {
                if(!vis[j]&&d[j]>min+map[p][j])
                    d[j] = d[i] + map[p][j];
            }//更新这点所连的点
        }
    }
}


Bellman-Ford:可以判断是否有负权

和dijkstra的区别:不知道每次更新的是哪个点
判断负权的方法:先求一次最短路,再进行同样的操作,如果一个点第n次又被更新了,说明存在负环/
 struct edge
 {
     int from;
     int to;
     int cost;
 };


edge e[maxn];//边
int d[maxn] //最短距离
int V,E;//顶点数,边数
int flag = 1;
void Bellman-Ford(int s)
{
    for(int i = 0; i < V; i++)
    {
        d[i] = INF;
    }
    d[s] = 0; 
    while(1)
    {
        flag = 0;
        for(int i = 0; i < E; i++)
        {
            edge ee = e[i];
            if(d[ee.from]!= INF&&d[ee.to]>d[ee.from] + ee.cost)
            {
                d[ee.cost] = d[ee.from] + ee.cost;
            }
            flag = 1;
        }
        if(!flag)
            break;
    }
}//求解源点到所有点的最短距离


bool find_negative_loop
{
    memset(d,0,sizeof(d));
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0; j < n; j++)
        {
            edge ee = e[j];
            if(d[ee.from]!= INF&&d[ee.to]>d[ee.from] + ee.cost)
            {
                 d[ee.cost] = d[ee.from] + ee.cost;
            
            if(i == V -1)//如果第n次更新了,则存在负权
                return true;
            }
            
        }
    }
    return false;
}


Floyd-Warshall:任意两点间的最短路,可以判断图中是否有负权
判断方法:检查是否存在d[i][j]为负数
i:起点  j:终点  k:顶点
分i到j的最短路正好经过顶点k一次和完全不经过k两种情况
d[i][j] = min(d[i][j],d[i][k]+d[k][j])
int d[maxn][maxn];
int V;
void warshall_floyd
{
    for(int k = 0; k < V; k++)
    {
        for(int i = 0; i < V; i++)
        {
            for(int j = 0; j < V; j++)
            {
                d[i][j] = min(d[i][j],d[i][k]+ d[k][j]);
            }
        }
    }
}


spfa+链式前向星
SPFA是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算,算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队



链式前向星
void add(int u,int v,int w)  
{  
    edge[cnt].to = v;  
    edge[cnt].next = head[u];  
    head[u] = cnt++;  
}  
1 2
2 3
3 4
1 3
4 1
1 5
4 5
edge[0].to = 2;     edge[0].next = -1;      head[1] = 0;
edge[1].to = 3;     edge[1].next = -1;      head[2] = 1;
edge[2].to = 4;     edge[2],next = -1;      head[3] = 2;
edge[3].to = 3;     edge[3].next = 0;       head[1] = 3;
edge[4].to = 1;     edge[4].next = -1;      head[4] = 4;
edge[5].to = 5;     edge[5].next = 3;       head[1] = 5;
edge[6].to = 5;     edge[6].next = 4;       head[4] = 6;
head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.


这样在遍历时是倒着遍历的,也就是说与输入顺序是相反的,不过这样不影响结果的正确性.
比如以上图为例,以节点1为起点的边有3条,它们的编号分别是0,3,5   而head[1] = 5
for(int i=head[u];~i;i=edge[i].next)


那么就是说先遍历编号为5的边,也就是head[1],然后就是edge[5].next,也就是编号3的边,然后继续edge[3].next,也
就是编号0的边,可以看出是逆序的.


spfa

如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

 


最短路

 

 

 

首先建立起始点a到其余各点的
最短路径表格

                                  最短路

首先源点a入队,当队列非空时:
 1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

                                  最短路

在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点
需要入队,此时,队列中新入队了三个结点b,c,d

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

                                 最短路

在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要
入队,此时队列中的元素为c,d,e

队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

                                 最短路

在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此
e不用入队了,f要入队,此时队列中的元素为d,e,f

 队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

 

                               最短路

在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g

队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:


                               最短路

在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e

队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

                           最短路

在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b
队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

                          最短路

在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

                         最短路

在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了

最终a到g的最短路径为14


bool spfa(){
  vis[1] = 1;
  queue<int> q;
  q.push(1);
  d[1] = 0;
  bool flag = false;
  while(!q.empty()){
    int t = q.front();
    q.pop();
    for(int i = id[t]; ~i; i = e[i].nex){
       // printf("u = %d, v = %d, d[%d] = %d, d[%d] = %d, len_uv = %d\n", t, e[i].v, t, d[t], e[i].v, d[e[i].v], e[i].len);
        if(d[e[i].v] > d[t] + e[i].len){
            d[e[i].v] = d[t] + e[i].len;
         //   printf("u = %d, v = %d, d[%d] = %d, d[%d] = %d\n", t, e[i].v, t, d[t] ,e[i].v ,d[e[i].v]);
            if(!vis[e[i].v]){
                vis[e[i].v] = 1;
                time[e[i].v]++;
                if(time[e[i].v] > n){ flag = true;}
                q.push(e[i].v);
            }
        }
    }
    vis[t] = 0;
  }

http://blog.****.net/zhhe0101/article/details/53871453         线段树

http://blog.****.net/liangzhaoyang1/article/details/51169090   k 最小生成树

http://blog.****.net/xunalove/article/details/70045815      spfa