您的位置:首页 > 其它

图论-1讲课总结

2015-07-20 16:15 106 查看
图论-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为从i到j的只以(1..k)集合中的节点为中间节点的最短路径的长度。

若最短路径经过点k,则Di,j,k = Di,k,k −
1 + Dk,j,k − 1;
若最短路径不经过点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相当于贪心加上并查集的一种实现

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

三.二分图
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: