当前位置: 首页 > news >正文

图论的整合

有若干个节点,有若干条边连接节点。(两个点之间不是必须相连)
比如:
在这里插入图片描述

有向图

可以理解为边上面有箭头的图,比如下面这张图:
在这里插入图片描述
在这张图中,点 111 可以通过这条有向边到达点 222,但是点 222 到达不了点 111
有向图的边是有单向性的。

无向图

只要连了边,边两端的点都可以互相到达。
在这里插入图片描述

这张图是无向图,里面任一点都可以互相到达。
这就是一张连通图。

连通图

一张无向图,如果任意一个点都可以到达图上的所有点,那么这张无向图就是连通图。
如图1

强连通图

一张有向图,如果任意两个点都可以互相到达,这就是一张强连通图。
在这里插入图片描述比如上面的这张图就是不联通的,不是强连通图。
如果再加上一条边:
在这里插入图片描述
现在这就是一张强连通图了。

带权图

可以理解为图中的边被加上了一个权值,经过这条边就要付出对应权值的代价。
比如:
在这里插入图片描述
在这张图中,点 111 走到点 444 的总权值是 3+5+2=103+5+2=103+5+2=10

图的存储

邻接矩阵

假设有一个 NNN 个点的图,我们可以开一个 N∗NN*NNN 的二维数组 aaaaija_{ij}aij 表示点 iii 到点 jjj 有没有边。
如果是带权图,也可以用 aija_{ij}aij 表示点 iii 到点 jjj 边的权值。

例题1

题目描述
给出一个无向图,顶点数为 nnn,边数为 mmmn≤1000,m≤10000n\le1000,m\le10000n1000,m10000
输入格式
第一行两个整数 n,mn,mn,m,接下来有 mmm 行,每行两个整数 u,vu,vu,v,表示点 uuu 到点 vvv 有一条边。
输出格式
由小到大依次输出每个点的邻接点,邻接点也按照由小到大的顺序输出。

首先 nnn 只有 100010001000,可以用邻接矩阵存图。
是无向图,所以 uuuvvvvvvuuu 都有一条边,所以要双向存。

#include<bits/stdc++.h>
#define psp putchar(' ')
#define endl putchar('\n')
using namespace std;
const int M=1e3+5;
int read(){int x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();return x*f;
}
void print(int x){if(x<0)putchar('-'),x=-x;if(x<10){putchar(x+'0');return;}print(x/10);putchar(x%10+'0');
}
int n,m;
int a[M][M];
signed main(){n=read(),m=read();for(int i=1;i<=m;i++){int u=read(),v=read();//双向存边a[u][v]=1;a[v][u]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(a[i][j])print(j),psp;//有边,输出。}endl;}
}
邻接表

假如一张图有 204820482048 个点,那么用邻接矩阵就要开 204822048^220482 的数组,那如果这张图只有 101010 条边,开这么大的空间就全浪费了。
这时候就要用到邻接表。
可以用动态数组来存点和边,减少空间的浪费。

vector<int>a[N];
例题2

题目描述
给出一个无向图,顶点数为 nnn,边数为 mmmn≤1000,m≤10000n\le1000,m\le10000n1000,m10000
输入格式
第一行两个整数 n,mn,mn,m,接下来有 mmm 行,每行两个整数 u,vu,vu,v,表示点 uuu 到点 vvv 有一条边。
输出格式
第i行输出第点 iii 的所有邻接点,邻接点按照度数由小到大输出,如果度数相等,则按照编号有小到大输出。

按照度数排序,用邻接表更方便。

#include<bits/stdc++.h>
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
int read(){int x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();return x*f;
}
void print(int x){if(x<0)putchar('-'),x=-x;if(x<10){putchar(x+'0');return;}print(x/10);putchar(x%10+'0');
}
int n,m;
vector<int>a[N];
int d[N];
bool cmp(int a,int b){//按照度数排序return d[a]<d[b]||(d[a]==d[b]&&a<b);
}
signed main(){n=read(),m=read();for(int i=1;i<=m;i++){int u=read(),v=read();//邻接表存图a[u].push_back(v);a[v].push_back(u);d[u]++,d[v]++;}for(int i=1;i<=n;i++){sort(a[i].begin(),a[i].end(),cmp);for(int j=0;j<a[i].size();j++)print(a[i][j]),psp;endl;}
}
例题3

题目描述
K(1≤K≤100)K(1\le K\le100)K(1K100) 只奶牛分散在 N(1≤N≤1000)N(1\le N\le1000)N(1N1000) 个牧场。现在她们要集中起来进餐。
牧场之间有 M(1≤M≤10000)M(1\le M\le10000)M(1M10000) 条有向路连接,而且不存在起点和终点相同的有向路。她们进餐的地点必须是所有奶牛都可到达的地方。
那么,有多少这样的牧场呢?
输入格式
第1行输入 K,N,MK,N,MK,N,M
接下来 KKK 行,每行一个整数表示一只奶牛所在的牧场编号。
接下来 MMM 行,每行两个整数,表示一条有向路的起点和终点。
输出格式
所有奶牛都可到达的牧场个数。

由于 NNN 最大只有 100010001000KKK 最大只有 100100100,所以可以考虑枚举每一头牛,然后记他们可以到达的点,最后的答案就有所有牛都能到达点的数量。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int read(){int x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();return x*f;
}
void print(int x){if(x<0)putchar('-'),x=-x;if(x<10){putchar(x+'0');return;}print(x/10);putchar(x%10+'0');
}
int n,m,k;
int cow[N];
vector<int>a[N];
int vis[N];
int dis[N];//dis[i]表示点i有几头牛可以到达
signed main(){k=read(),n=read(),m=read();for(int i=1;i<=k;i++)cow[i]=read();while(m--){int u=read(),v=read();a[u].push_back(v);}for(int i=1;i<=k;i++){queue<int>q;for(int j=1;j<=n;j++)vis[j]=0;vis[cow[i]]=1;q.push(cow[i]);while(!q.empty()){int x=q.front();q.pop();dis[x]++;//记录for(int p=0;p<a[x].size();p++){int y=a[x][p];if(vis[y])continue;vis[y]=1;q.push(y);}}}int ans=0;for(int i=1;i<=n;i++)ans+=(dis[i]==k);print(ans);
}
例题4

P3144 [USACO16OPEN] Closing the Farm S
依次遍历每个仓库关闭,检查是否能遍历到除关闭仓库外的所有仓库。

#include<bits/stdc++.h>
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
int read(){int x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();return x*f;
}
void putstr(string s){for(char v:s)putchar(v);
}
int n,m;
vector<int>a[N];
int cl[N];
int vis[N];
signed main(){n=read(),m=read();while(m--){int u=read(),v=read();a[u].push_back(v);a[v].push_back(u);}int st=1;for(int i=1;i<=n;i++){while(cl[st])st++;queue<int>q;q.push(st);for(int i=1;i<=n;i++)vis[i]=0;vis[st]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=0;i<a[x].size();i++){int y=a[x][i];if(vis[y])continue;if(cl[y])continue;vis[y]=1;q.push(y);}}bool flag=1;for(int i=1;i<=n;i++){if(cl[i])continue;if(!vis[i])flag=0;}cl[read()]=1;if(flag)putstr("YES");else putstr("NO");endl;}
}

最短路

一张带权图,一个点 xxx 到另一个点 yyy 所经过边的最小权值和称为点 xxx 到点 yyy 的最短路。

Dijkstra

一种高效的求最短路的算法,缺点是不能求带负权的最短路。
它的主要用途是求一个点到图中其他点的最短路。
具体过程
最开始,除了起点外所有点没有被标记过,起点是最开始被选中的点。
开始模拟:找到被选中的点连接的最小边权的且没有被标记过的点 yyy,记录最短路,然后点 yyy 被标记,并成为下一个被选中的点,直到所有点都被标记。

例题5

P4779 【模板】单源最短路径(标准版)
具体代码见单源最短路径

SPFA

讲解+例题见带负权的最短路问题


  1. 无向图 ↩︎

http://www.lryc.cn/news/595668.html

相关文章:

  • JS WebAPIs DOM节点概述
  • v0+claude+cursor构建初始脚手架
  • 北京养老金计算公式网页实现案例:从需求分析到架构设计
  • 在Python中操作Word
  • 滴滴0722 总结与优化方向
  • J2EE模式---前端控制器模式
  • es6中的symbol基础知识
  • Element Plus Table 组件扩展:表尾合计功能详解
  • UE5 UI ScrollBox 滚动框
  • .NET使用EPPlus导出EXCEL的接口中,文件流缺少文件名信息
  • 归并排序(Merge Sort)(递归写法)
  • 【前端】ikun-pptx编辑器前瞻问题一: pptx的xml样式, 使用html能100%还原么
  • vscode目录,右键菜单加入用VSCode打开文件和文件夹(快速解决)(含删除)(脚本)
  • 基于 KeepAlived + HAProxy 搭建 RabbitMQ 高可用负载均衡集群
  • 医院信息系统(HIS)切换实施方案与管理技术分析
  • Linux中信号认识及处理和硬件中断与软中断的讲解
  • 基于 Spring Batch 和 XXL-Job 的批处理任务实现
  • iOS加固工具有哪些?从零源码到深度混淆的全景解读
  • iOS 抓包工具有哪些?场景导向下的工具推荐与实战对比
  • 微软徽标认证是什么?如何快速获取驱动签名?
  • haproxy七层代理新手入门详解
  • 字体识别实战:用Python打造智能字体侦探工具
  • 查看 iOS iPhone 设备上 App 和系统运行时的实时日志与崩溃日志
  • 一文速通《线性方程组》
  • ipynb断点不停 ipynb调试相关
  • 项目集成zustand后,如何构建和使用,以及devtools函数。
  • 报错error:0308010C:digital envelope routines::unsupported解决方案
  • 网络原理 HTTP 和 HTTPS
  • 【3GPP】5G专用词汇1
  • 开源AI智能客服、AI智能名片与S2B2C商城小程序在客户复购与转介绍中的协同效应研究