2.5 图

2.5.1 图是什么

  1. n个顶点的无向图要连通至少需要(n-1)条边
    E.g.2019山东省赛D
  2. 一棵树的边数 = 定点数 - 1
    边数 = 定点数 - 1 的连通图是树

无圈的有向图叫DAG(Directed Acyclic Graph),在DAG中对每个顶点给出一个编号,第i号顶点叫做vi。那么存在vi到vj的边时就有i<j成立。这样的编号方式叫做拓扑序。
有些DAP问题可以用dp解决。求解拓扑序的算法叫做拓扑排序。

2.5.2 图的表示

  1. 邻接矩阵
    使用|V|x|V|的二维数组存储。无向图必有g[i][j] = g[j][i]。
    在带权图中,令g[i][j] = INF,通常保存权值最小/最大的边即可。

  2. 邻接表
    在边数稀少时相交邻接矩阵占用内存更少,但实现起来更复杂。
    并且,在邻接表中查询两点之间是否有边需要遍历一遍链表才能知道。

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;

#define MAX_V 10000

vector<int> G[MAX_V];
/*
边上有属性的情况
struct edge
{
    int to, cost;
};
vector<edge> G[MAX_V];
*/

int main()
{
    int V, E;
    scanf ("%d %d", &V, &E);
    for(int i = 0;i < E;i++)
    {
        //s --> p
        int s, t;
        scanf("%d %d", &s, &t);
        G[s].push_back(t);
        //如果是无向图,则需要再从t向s连边
    }

    return 0;
}

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;

#define MAX_V 10000

struct vertex
{
    vector<vertex*> edge;
    /*
    顶点的属性
    */
};
vertex G[MAX_V];

int main()
{
    int V, E;
    scanf("%d %d", &V, &E);
    for(int i = 0;i < E;i++)
    {
        int s, t;
        scanf("%d %d", &s, &t);
        G[s].edge.push_back(&G[t]);
        //G[t].edge.push_back(&G[s]);
    }
    /*
    图的操作
    */

    return 0;
}

2.5.3 图的搜索

/*二分图判定*/

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;

#define MAX_V 10000

vector<int> G[MAX_V]; //graph
int V;                //vertex sum
int color[MAX_V];     //顶点i的颜色(1, -1)

bool dfs(int v, int c)
{
    color[v] = c; //把顶点v染成颜色c
    for(int i = 0;i < G[v].size();i++)
    {
        //相邻顶点同色
        if(color[G[v][i]] == c)
            return false;
        //尚未染色,染色为-c
        if(color[G[v][i]] == 0 && !dfs(G[v][i], -c))
            return false;
    }

    //循环能完整结束说明所有顶点均被染色过
    return true;
}

int main()
{
    scanf("%d", &V);
    for(int i = 0;i < V;i++)
    {
        if(color[i] == 0)
        {
            //如果顶点i尚未染色,则染色为1
            if(!dfs(i, 1))
            {
                printf("No\n");
                return;
            }
        }
    }
    printf("Yes\n");
    return 0;
}

2.5.4 最短路

  • 单源最短路问题
    存在负边: Bellman-Ford, Floyd-Warshall(复杂度较低时)
    不存在负边:Dijkstra

Bellman-Ford

求最短路,检查负图

#include <iostream>

using namespace std;

#define MAX_E 1000

struct edge
{
    int from,
        to,
        cost;
    //由from顶点指向to顶点 值为cost的边
};

edge es[MAX_E]; //边

int d[MAX_V];   //最短距离
int V,          //顶点数
    E;          //边数


//求解从顶点s出发到所有点的最短距离
void shortest_path(int s)
{
    for(int i = 0;i < V;i++)
        d[i] = INF;

    d[s] = 0;

    while(true)
    {
        bool update = false;
        for(int i = 0;i < E;i++)
        {
            edge e = es[i];
            if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost)
            {
                d[e.to] = d[e.from] + e.cost;
                update = true;
            }
        }
        if(!update)
            break;
    }
}

Dijkstra

使用邻接矩阵实现,复杂度为O(|V^2|)

#include <iostream>

using namespace std;

#define MAX_V 10000

int cost[MAX_V][MAX_V];  //cost[u][v]表示边e=(u,v)的权值(不存在这条边时设为INF)
int d[MAX_V];            //顶点s出发的最短距离
bool used[MAX_V];        //已经使用过的图
int V;

//求从起点出发到各个顶点的最短距离
void dijkstra(int a)
{
    fill(d, d+V, INF);
    fill(used, used+V, false);
    d[s] = 0;

    while(true)
    {
        int V = -1;
        //从尚未使用过的顶点中选择一个距离最小的顶点
        for(int u = 0;u < V;u++)
        {
            if(!used[u] && (V == -1 || d[u] < d[v]))
                v = u;
        }
        if(v == -1)
            break;
        used[v] = true;

        for(int u = 0;u < V;u++)
        {
            d[u] = min(d[u], d[v] + cost[v][u]);
        }
    }
}

用堆优化数值的插入(更新)和取出最小值两个操作

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

#define MAX_V 10000
#define INF 99999999

struct edge
{
    int to,
        cost;
};
typedef pair<int, int> P; //first为最短距离,second为顶点编号

int V;
vector<edge> G[MAX_V];
int d[MAX_V];

void dijkstra(int s)
{
    //通过指定greater<P>参数,堆按照从小到大顺序排序
    priority_queue<P, vector<P>, greater<P> > que;
    fill(d, d + V, INF);
    d[s] = 0;
    que.push(P(0, s));

    while(!que.empty())
    {
        P p = que.top();
        que.pop();
        int v = p.second;
        if(d[v] < p.first)
            continue;
        for(int i = 0;i < G[v].size();i++)
        {
            edge e = G[v][i];
            if(d[e.to] > d[v] + e.cost)
            {
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to], e.to));
            }
        }
    }

}

Floyd-Warshall

求解任意两点间的最短路

#include <iostream>

using namespace std;

#define MAX_V 1000

int d[MAX_V][MAX_V];    //d[u][v]表示e=(u,v)的权值(不存在时设置为INF,不过d[i][i] = 0)
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]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值