图的最短路径及最小生成树 模板

本文来自《挑战程序设计竞赛》2.5 它们其实都是图

1.图的搜索

1.题目原文:

二分图判定。给定一个具有n个顶点的图,要给图上每个顶点染色,并且要使相邻的顶点颜色不同。问是否能最多用两种颜色进行染色。题目保证没有重边和自环。

1<=n<=1000

2.分析:

把相邻顶点染成不同颜色的问题叫作图的着色问题。对图进行染色所需要的最小颜色数成为最小着色数。最小着色数是2的图称作二分图。
如果只用两种颜色进行染色,那么确定一个顶点后,和它相邻的顶点也就确定了。因此可以从任意顶点出发,就可以判断是否可以被2种颜色染色了。用深度优先搜索很容易实现。

3.代码及样例

/*
3
0 1
0 2
1 2
-1 -1

NO

4
0 1
0 3
1 2
2 3
-1 -1
YES
*/
#include 
#include
#include
using namespace std;
#define maxv 1000
vector G[maxv];//图
int V;//顶点数
int color[maxv];//顶点i的颜色
//将顶点染成1或-1
bool dfs(int v,int c)
{
    color[v]=c;//把顶点v染成颜色c
    for(int i=0;i>V;
    int u,v;
    cin>>u>>v;
    while(u>=0&&v>=0){
        G[u].push_back(v);
        G[v].push_back(u);
        cin>>u>>v;
    }
    memset(color,0,sizeof(color));
    solve();
    return 0;
}

2.最短路径问题

最短路是给定两个顶点,在以这两个顶点为起点和终点的路径中,边的权值之和最小的路径。智力游戏中的最小步数,也可以看作最短路径问题。

1.单源最短路径1(Bellman-Ford算法)

1.算法原理

记从起点s出发到顶点i的最短距离为d[i],则有d[i]=min{d[j]+(从j到i的边的权值)|e=(j,i)属于E}

如果给定的图是DAG,就可以按拓扑序给顶点编号,按照上述递推关系式计算出d。但是图中如果有圈,就不可以依赖这样的顺序计算。在这种情况下,设初值d[s]=0,d[i]=INF(足够大的常数),在不断利用这个递推关系式就可以解决。只要图中不存在负圈,操作就是有限的。


2.代码及样例:

/*
5 10
0 1 6
0 3 7
1 2 5
1 3 8
1 4 -4
2 1 -2
3 2 -3
3 4 9
4 0 2
4 2 7

0 2 4 7 -2
*/
#include 
#include
#include
using namespace std;
#define maxn 1000
#define INF 0x7fffffff
int V,E;
struct edge
{
    int from;
    int to;
    int cost;
};
edge es[maxn];//边
int d[maxn];//最短距离
//求解从顶点s出发到所有顶点的最短距离
void shortest_path(int s)
{
    for(int i=0;id[u]+e.cost){
                d[v]=d[u]+e.cost;
                update=true;
            }
        }
        if(!update) break;
    }
}
int main()
{
    cin>>V>>E;
    for(int i=0;i>u>>v>>c;
        es[i]=(edge){u,v,c};//这个感觉好6,以前没见过……
    }
    shortest_path(0);
    for(int i=0;i

3.时间复杂度分析

如果图中不存在从s可达的负圈,那么最短路不会经过一个顶点两次(也就是说最多通过|V|-1条边,while循环最多执行|V|-1次。如果存在从s可达的负圈,那么第|V|次循环也会更新d的值,可以用这个性质,检查负圈。如果一开始把d[i]全部初始化成0,就可以检查所有的负圈。

4.负圈检查

//检查是否存在负圈
bool find_negative_loop()
{
    memset(d,0,sizeof(d));
    for(int i=0;id[e.from]+e.cost){
                d[e.to]=d[e.from]+e.cost;
                //如果第n次仍然更新了,则存在负圈
                if(i==V-1) return false;
            }
        }
    }
    return true;
}

3.最小生成树

2.Kruscal算法

1.算法原理

Kruscal算法按照边的权值的顺序从小到大查看一遍,如果不产生圈,就把这条边加入到生成树中。

现在介绍如何判断是否产生边。假设现在要把连接顶点u和v的边e加入到生成树中,如果之前u和v不在同一个连通分量中,那么加入e不会产生圈,否则会。可以用并查集高效地判断是否属于同一个连通分量。

Kruscal算法在边的排序上最花时间,算法的时间复杂度为O(ElogV)。

2.代码模板

/*
9 14
0 1 4
0 7 8
1 2 8
1 7 11
2 3 7
2 5 4
2 8 2
3 4 9
3 5 14
4 5 10
5 6 2
6 7 1
6 8 6
7 8 7

37
*/
#include
#include
#include
#include
using namespace std;
#define maxn 1005
int par[maxn];//父亲
int Rank[maxn];//树的高度
//初始化
void init(int n)
{
    for(int i=0;i>V>>E;
    for(int i=0;i>u>>v>>c;
        es[i]=(edge){u,v,c};
    }
    printf("%d\n",kruscal());
    return 0;
}

你可能感兴趣的:(图论,数据结构,挑战程序设计竞赛,最小生成树)