当前位置: 首页 > 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.dtcms.com/a/292014.html

相关文章:

  • 西门子 S7-1500分布式 I/O通信 :PROFINET IO 与 PROFIBUS DP核心技术详解(上)
  • Spring、Spring MVC、Spring Boot、Spring Cloud的联系和区别
  • Uni-App:跨平台开发的终极解决方案
  • uniapp app打包流程
  • 华为服务器操作系统openEuler介绍与安装
  • uniapp 报错 Not found ... at view.umd.min.js:1的问题
  • Kafka——揭开神秘的“位移主题”面纱
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现持械检测(C#代码,UI界面版)
  • 记一次flink资源使用优化
  • 《Uniapp-Vue 3-TS 实战开发》自定义时间选择
  • @DateTimeFormat、@JsonFormat、@JSONField区别及用法
  • 获取制定版本的yolov5,使用下载zip文件替代git
  • C++核心编程学习4--类和对象--封装
  • J2EE模式---前端控制器模式
  • 智慧能源合同解决方案
  • Apache Ignite 中乐观事务(OPTIMISTIC Transactions)的工作机制
  • WAF 防护与漏洞扫描联动:让安全防御更精准高效
  • 50期权交易的典型例子
  • K 近邻算法(K-Nearest Neighbors, KNN)详解及案例
  • MySQL 学习二 MVCC
  • 【时时三省】(C语言基础)指向函数的指针
  • SpringCloud Nacos配置中心
  • CentOS 8文件描述符耗尽检测与处理实战指南
  • Linux CentOS 虚拟机升级内核至4.x以上版本
  • 为何在 Vue 的 v-model 指令中不能使用可选链(Optional Chaining)?
  • AI-调查研究-35-咖啡价格战 味觉与消费体验差异:自制咖啡为何更“好喝”?
  • 【Practical Business English Oral Scene Interpretation】 No9~10
  • vue 用hbuilder打包apk后返回键不好使
  • importlib.import_module() 的用法与实战案例
  • 重构创作边界:川翔云电脑 - UE5云端超算引擎​