图论-1讲课总结

图论-1主要讲了一下内容:

1.单源最短路径dij,多源最短路径floyd,负权图spfa

2.最小生成树 Prime,Kruskal

3.二分图,最小点覆盖,最大独立集

4.割点,割边。

下面就来详细写一下个部分内容,以免混淆。


一. 最短路径

1.首先是单源最短路径(无负权)的Dijskatra算法,该算法是基于贪心思想的,算法思想如下:
我们定义一个点集S,距离dis[]。点集S初始只有源点,dis[]初始化成源点到其他节点的距离。
算法思想就是每次找到未加入S中的dis[]中的最小值,将其加入S中同时维护dis[]数组。就相当于把加入S点集中的点缩成一个点,就有相当于源点是的情况啦。这就是他的最有子结构与贪心策略。

Step1:初始化dis[]数组,源点加入S

Step2:查询不在S中且dis[]最小的

Step3:根据新加入的点更新dis

Step4:如果还有未加入S中的点,鸡血Step2.

下面给出邻接表的实现

#define INF 0xffffffff
const int MAXN=1000;        ///MAXN是节点的数目
typedef struct{
    int to,cost;
}node;
vector<node> Grap[MAXN];     ///Grap[i]中存储的是i的邻接表。
int dis[MAXN];
bool vis[MAXN];

void Dij(int s){///源点是s
    memset(vis,0,sizeof(vis));
    for(int i=0;i<MAXN;i++)
        dis[i]=INF;
    dis[s]=0;
    for(int i=0;i<MAXN;i++){
        int Min=INF,pos;
        for(int j=0;j<MAXN;j++){
            if(Min>dis[j]&&!vis[j]){
                pos=j;
                Min=dis[j];
            }
        }
        vis[pos]=1;
        for(int j=0;j<Grap[pos].size();j++){
            int t=Grap[pos][i].to;
            dis[t]=min(dis[t],dis[pos]+Grap[pos][i].cost);
        }
    }
}

2多源最短路径floyd

Floyd-Warshall算法的原理是动态规划

Di,j,k为从ij的只以(1..k)集合中的节点为中间节点的最短路径的长度。

  1. 若最短路径经过点k,则Di,j,k = Di,k,k − 1 + Dk,j,k − 1
  2. 若最短路径不经过点k,则Di,j,k = Di,j,k − 1

因此,Di,j,k = min(Di,k,k − 1 + Dk,j,k − 1,Di,j,k − 1)

在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。(见下面的算法描述)

Floyd-Warshall算法的描述如下:

for k  1 to n do
  for i  1 to n do
    for j  1 to n do
      if (Di,k + Dk,j < Di,j) then
        Di,j  Di,k + Dk,j

其中Di,j表示由点i到点j的代价,当Di,j为 ∞ 表示两点之间没有任何连接。 

下面是代码实现:

for ( int k = 0 ; k < n ; k++ )
           for ( int i = 0 ; i < n ; i++ )
               for  ( int j = 0 ; j < n ; j++ )
                       dis[i][j] = min ( dis[i][j] , dis[i][k] + dis[k][j] );
3.对负权图的单源最短路径SPFA

int spfa_bfs(int s)
{
    queue <int> q;
    memset(d,0x3f,sizeof(d));
    d[s]=0;
    memset(c,0,sizeof(c));
    memset(vis,0,sizeof(vis));

    q.push(s);  vis[s]=1; c[s]=1;
    //顶点入队vis要做标记,另外要统计顶点的入队次数
    int OK=1;
    while(!q.empty())
    {
        int x;
        x=q.front(); q.pop();  vis[x]=0;
        //队头元素出队,并且消除标记
        for(int k=f[x]; k!=0; k=nnext[k]) //遍历顶点x的邻接表
        {
            int y=v[k];
            if( d[x]+w[k] < d[y])
            {
                d[y]=d[x]+w[k];  //松弛
                if(!vis[y])  //顶点y不在队内
                {
                    vis[y]=1;    //标记
                    c[y]++;      //统计次数
                    q.push(y);   //入队
                    if(c[y]>NN)  //超过入队次数上限,说明有负环
                        return OK=0;
                }
            }
        }
    }

    return OK;

}
二.最小生成树

1.最小生成树的Prime算法

Prime算法也是基于贪心的思想,和dij很想,不过不同的是dis维护的是做过的路径长度最短,Prime算法维护的是加入的单个边的长度最短。

2最小生成树的Kruskal算法

Kruskal相当于贪心加上并查集的一种实现

对边的长度进行排序,然后依次检索每一条边,如果当前检索的边不在同一个连通分量里,就把这条边加入,将他们合并在一起,依次进行这个操作。

三.二分图




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值