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

图论的整合

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

有向图

可以理解为边上面有箭头的图,比如下面这张图:
在这里插入图片描述
在这张图中,点 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.xdnf.cn/news/16029.html

相关文章:

  • 前端--bom、JQuery
  • 大数据量查询计算引发数据库CPU告警问题复盘
  • WAF 防护与漏洞扫描联动:让安全防御更精准高效
  • python办自动化--读取邮箱中特定的邮件,并下载特定的附件
  • 数据库—修改某字段默认值
  • importlib.import_module() 的用法与实战案例
  • Java值传递和构造函数
  • Java 并发性深度解析
  • C# 基于halcon的视觉工作流-章21-点查找
  • 【前端】ikun-pptx编辑器前瞻问题一: pptx的xml样式, 使用html能100%还原么
  • 【计算机网络 篇】TCP基本认识和TCP三次握手相关问题
  • 基于springboot的医院后台管理系统的设计与实现(源码+论文)
  • 【python数据结构算法篇】python算法
  • Ubuntu 虚拟机配置 与Windows互传文件
  • 零事故网站重构:11步标准化流程与风险管理指南
  • PHICOMM(斐讯)N1盒子 - Armbian25.05(Debian 12)刷入U盘/EMMC
  • 【Spring Boot】Spring Boot循环依赖破解:@Lazy与Setter注入的取舍指南(流程图修复版)
  • Oracle RAC+ADG switchover 切换演练流程
  • 【文献笔记】ARS: Automatic Routing Solver with Large Language Models
  • LabVIEW 2025安装包| 免费免激活版下载| 附图文详细安装教程
  • Tailwind CSS快速上手 Tailwind CSS的安装、配置、使用
  • 使用qt编写上位机程序,出现串口死掉无法接受数据的bug
  • 【windows修复】解决windows10,没有【相机] 功能问题
  • 前端学习 4:一些术语集合
  • 自研能管项目开发界面
  • uniapp “requestPayment:fail [payment支付宝:62009]未知错误“
  • Gerrit多仓库对应多邮箱配置办法
  • 上下文工程的系统性优化:从组件到整合架构
  • 【ArcGIS Pro】设置临时存储文件夹(计算缓存数据存放位置)
  • 网络安全实验 番外篇 使用Web登录eNSP中防火墙