图论day1

来源:互联网 发布:mac 中断 ping 编辑:程序博客网 时间:2024/05/21 04:22

说实话,今天学习图论的时候状态不是特别的好,上午马马虎虎过得去,下午简直听不进去。

上午

上午主要是学习了图的相关基本概念和知识,外加图的dfs遍历和图的bfs遍历。

例题:luoguP3916图的遍历。
题目描述

给出NN个点,MM条边的有向图,对于每个点vv,求A(v)A(v)表示从点vv出发,能到达的编号最大的点。

输入输出格式

输入格式:
第1 行,2 个整数N,MN,M。

接下来MM行,每行2个整数U_i,V_iU
​i
​​ ,V
​i
​​ ,表示边(U_i,V_i)(U
​i
​​ ,V
​i
​​ )。点用1, 2,\cdots,N1,2,⋯,N编号。

输出格式:
N 个整数A(1),A(2),\cdots,A(N)A(1),A(2),⋯,A(N)。

输入输出样例

输入样例#1:
4 3
1 2
2 4
4 3
输出样例#1:
4 4 3 4
说明

• 对于60% 的数据,1 \le N . K \le 10^31≤N.K≤10
​3
​​ ;

• 对于100% 的数据,1 \le N , M \le 10^51≤N,M≤10
​5
​​ 。
这道题目在60%以内是一道很水的图的遍历的问题,只要用dfs遍历(或者可以运用链表的相关用法来做),再用一个maxx之类的变量来记录本条路径上的最大值就ok了。
代码如下。
#include<bits/stdc++.h>
using namespace std;
int n,m;
bool a[10200][10200];
bool f[200000];
void read()//读入
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
//读入x到y的新路径
a[x][y]=true;
//x到y标记为可以到
}
}
int maxx;
//用来记录当前结点开始能达到的最大结点编号
void hy(int k)//dfs
{
for(int i=1;i<=n;i++)
if(f[i]==false&&a[k][i])//如果没有访问过
//且从k到这个结点有路可走
{
f[i]=true;//那就标记为在访问过了
maxx=max(maxx,i);//求最大值
hy(i);//从第i个点开始继续
}
}
int main()
{
memset(a,false,sizeof(a));
read();//读入
for(int i=1;i<=n;i++)
{
memset(f,false,sizeof(f));//每次循环都要对f数组进行初始化。
maxx=i; //maxx赋初值
f[i]=true;//第一个点标记为已经访问过
hy(i);//开始深搜
printf("%d ",maxx);
}
return 0;
}

下午

下午讲了三种图论算法。具体题目暂时还没刷完,待明日再续

总结

  1. 几种图的表示方法
    邻接矩阵:用二维数组实现,对于每一个元素a[i][j],0表示i到j没有通路,其他数表示有通路(或者该边的权值),一般地,我们会对没有通路的边赋予很大很大的值。

    邻接表:用链表来实现。可以用习惯上的链表(较容易出错),也可以用静态链表(用一个数组充当链表的头指针数组,再用一个数组来存储每一个结点的编号、权值、后继,实在是跟习惯上的指针链表太像了呢)。

struct edge{    int y,v,next;       //y表示这条边的终点编号,v是权值;};                  //next表示同起点下条边的编号是多少edge e[maxm+10];  //边表。int linkk[maxn+10];  //起点表 link[i]表示由i出去的第一条边的下标是多少void insert(int ss,int ee,int vv)//ss为起点,ee为终点,vv为权值。{      e[++t].y=ee; e[t].v=vv;     //t表示有t条边,是个全局变量。    e[t].next=linkk[ss]; linkk[ss]=t;}void init(){    scanf("%d %d %d",&n,&p,&m);    for (int i=0;i<m;i++)    {        int xx,yy,zz;        scanf("%d%d%d",&xx,&yy,&zz);        insert(xx,yy,zz);        insert(yy,xx,zz);  //这里插入的是无向图,所以两条边都要插入。    }    }    

对于邻接表,我们遍历的方法可以参照链表访问的方法。

for (int i=linkk[k];i;i=e[i].next)

用这样一个for语句就可以实现了

边表:用每一个元素来存储一条边的起点、终点、权值。

Struct  edge{    int x,y  //起点和终点    int v    //权值}  e[maxm];e[i].x;  e[i].y   e[i].v
  1. 三种方法的辨析
    邻接矩阵:代码书写简单,找邻接点慢
    采用二维数组的静态存储结构
    一般点数|v|小于 等于5000的时候,用邻接矩阵。

邻接表:代码书写较复杂,找邻接点快
采用动态存储结构(指针或用数组模拟)
一般点数|v|大于等于5000,并且边得个数不是很多的时候,用邻接表,并且现在一般都是用数组来模拟。
数组模拟链表的速度会快一点,并且能避免一些错误。
边表可以用于跟边关系较大的题目。

  1. 图的遍历
    图的遍历,我们学习了dfs深度优先遍历和bfs广度优先遍历,dfs遍历和深度优先搜索和回溯算法很像,就是缺少了回溯这一步(我们只关心是否到达了没一点,但是不关心到达这条路的路径长短,所以不用回溯算法)。
原创粉丝点击