图算法专题

目录

图的遍历

DFS算法(深度优先)

BFS算法(广度优先)

最短路径

Dijkstra算法

最小生成树

prim算法

Kruskal


图的遍历

DFS算法(深度优先)

在一个连通块里遍历:

void DFS(int r){
   vis[r] = true;
   for(int i=0;i<num;i++){
       if(vis[i] == false && G[r][i] < INF)
           DFS(i);
    }
}

可以在这部分加上深度等信息。

遍历所有的连通块:

void DFSTrave(G){
    for(int i=0;i<num;i++){
        if(vis[i] == false)
            DFS(i);
     }
}      

可以在这部分计算有几个连通块。

BFS算法(广度优先)

void BFS(int r){
   queue<int> q;
   q.push(r);
   vis[r] = true;
   while(!q.empty()){
       int now = q.front();
       for(int i=0;i<num;i++){
           if(vis[i]==true;G[i][now]<INF)
               {q.push(i);
                vis[i] = true;}
        }
    }
}

可以看到,区别于DFS,BFS算法是没有递归的。

遍历图G:

void BFSTrave(){
   for(int i=0;i<num;i++){
       if(vis[i]==false)
            BFS(i);
   }
}

同样可以计算有几个连通块。

最短路径

Dijkstra算法

给定图G和起点s,通过算法得到S到达其他每个顶点的最短距离。而Dijkstra算法的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S。令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。操作n次(n为顶点个数),直到集合S包含所有顶点。

需要设置全局变量:

#define INF 1000000
#define maxn 1000

//必须
int n;//n个城市
int vis[maxn];//是否访问
int G[maxn][maxn];//两点之间距离
int dis[maxn];//起点到各点最短距离

//非必须,根据题目
//比如有些要求在有多条路径最短线路时选择某指标最高的路径,比如有些需要输出具体路径
int val[maxn];//起点到各点可得最高指标
int num[maxn];//各点某指标
int pre[maxn];//起点到该点最短路径上的前一个顶点

具体算法:

void Dirkstra(int s)//s为起点
{
    for(int i=0;i<n;i++){
       dis[i] = G[s][i];
    }
    dis[s] = 0;
    for(int i=0;i<n;i++){
       int k=-1, min = INF;
       for(int j=0;j<n;j++){
           if(vis[j]==0&&dis[j]<min)
               { min = dis[j]; k=j;}
       }
       if(k==-1) return;
       vis[k] == 1;
       for(int j=0;j<n;j++){
            if(vis[j]==0 && G[k][j] != INF)
            {    if(dis[j]>dis[k]+G[k][j]) {
                       dis[j] = dis[k]+G[k][j];
                       val[j] = val[k] + num[j];
                       pre[j] = k;
                 }
                 else if(dis[j]==dis[k]+G[k][j]){
                        if(val[j] < val[k] + num[j]){
                        val[j] = val[k] + num[j];
                        pre[j] = k;
                        }
                 }
            }
       }
    }
}

上面把路径也存储了。如果要读取路径,可以用DFS算法进行递归:

void path_DFS(int s, int v)//s为起点,v为当前编号(从终点开始递归)
{
    if(v==s) {
      printf("%d\n", v);
      return;}
    path_DFS(s, pre[v]);
    printf("%d\n", v);
}

 可以在Dirkstra算法中也可以在main函数中设置全局变量:

memset(G,INF,sizeof(G));
memset(vis,0,sizeof(vis));
memset(dis,INF,sizeof(dis));//这个可以不设置

最小路径算法除了Dirkstra,还有Bellman-Ford算法,SPFA算法,Floyd算法等。Bellman-Ford算法可以处理负边权的问题,而Dirkstra不可以。

最小生成树

最小生成树(MST)是在一个给定的无向图G(V,E)中求一棵树,使得这棵树拥有图G中所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。

最小生成树的性质:

  • 边数等于顶点数减一,树内没有环
  • 对给定图G(V,E),最小生成树不唯一,但其边权之和唯一
  • 根结点可以是树上任意一个结点

最短路径与最小生成树的区别在于:

最短路径是找到两个点之间的最短路径,最小生成树是找到能把若干个离散点连起来的最短路径。

prim算法

基本思想:对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。令顶点u为中介点,优化所有从u能到达的顶点v与集合S之间的最短距离。操作执行n次(n为顶点个数),直到集合S已包含所有顶点。

设置全局变量:

#define maxn 1000
#define INF 1000000

int n;//n个离散点
int G[maxn][maxn];
int dis[maxn];
int vis[maxn];

prim算法:

int prim(){//默认0号为初始点,函数返回最小生成树的边权之和
    fill(dis,dis+maxn,INF);
    dis[0] = 0;
    int ans = 0;//存放最小生成树的边权之和
    for(int i=0;i<n;i++){
        int k=-1,min=INF;
        //查找未被访问的离集合S最近的点
        for(int j=0;j<n;j++){
            if(vis[j]==0&&dis[j]<min){
                min = dis[j];
                k=j;
            }
        }
        if(k==-1) return;
        vis[k] = 1;
        ans += dis[k];
        for(int j=0;j<n;j++){
            if(vis[j]==0 && G[k][j] != INF && dis[j]>G[k][j]) //为了让该点到集合S更近
               {   dis[j] = G[k][j]; }
        }
      }
     return ans;
}

区别于Dirkstra算法,prim是找到距离集合S最近的点,优化也是为了到集合S最近,而非到初始点最近。

Kruskal

最小生成树还可以用Kruskal算法来解决。需要注意的是,稠密图用prim算法,稀疏图用kruskal算法。可以根据题目所给出的数据来进行选择。

算法sicily例题 1000. sicily 1155. Can I Post the lette Time Limit: 1sec Memory Limit:32MB Description I am a traveler. I want to post a letter to Merlin. But because there are so many roads I can walk through, and maybe I can’t go to Merlin’s house following these roads, I must judge whether I can post the letter to Merlin before starting my travel. Suppose the cities are numbered from 0 to N-1, I am at city 0, and Merlin is at city N-1. And there are M roads I can walk through, each of which connects two cities. Please note that each road is direct, i.e. a road from A to B does not indicate a road from B to A. Please help me to find out whether I could go to Merlin’s house or not. Input There are multiple input cases. For one case, first are two lines of two integers N and M, (N<=200, M<=N*N/2), that means the number of citys and the number of roads. And Merlin stands at city N-1. After that, there are M lines. Each line contains two integers i and j, what means that there is a road from city i to city j. The input is terminated by N=0. Output For each test case, if I can post the letter print “I can post the letter” in one line, otherwise print “I can't post the letter”. Sample Input 3 2 0 1 1 2 3 1 0 1 0 Sample Output I can post the letter I can't post the letter Source Code #include #include using namespace std; int n,m; vector vout[200]; bool visited[200]; bool flood(int u) { visited[u]=1; if (u==n-1) return 1; for (int x=0; x<vout[u].size(); x++) { int &v=vout[u][x]; if (!visited[v] && flood(v)) return 1; } return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲸鲸爱柠檬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值