-->

图论笔记

 

图的概念

图:点--边

度->有向图(入度,出度)|| 无向图(度)

自环:若一条边的两个顶点为同一顶点则此边称作自环。

路径:从任何一个点出发,随意在图上走,走出来的序列叫路径。| 简单路径(一条路径,每个点最多只能走一次)


 

特殊的图

1)没有环的无向图:树-->无向,连通,无环   || n个点 n-1条边

只有无向无环:很多个树-->森林

只有连通无环(有向):(连通的DAG)/有向树

只有无向连通:。。。

2)有向图的树:外向树,内向树

外向:所有边的方向都是从根向外指

 内向:所有边的方向都是从外向根的方向指

 

3)树的扩展:章鱼图/基环树:环上每个点连出去的部分为一棵树 || n个点的章鱼图n条边

树变成章鱼图加一条边 || 章鱼图删掉环上一条边变成树

4)仙人掌图:边仙人掌,点仙人掌

边仙人掌:每条边最多在一个环上

点仙人掌:每个点最多在一个环上

点仙人掌一定是边仙人掌

5)DAG(Directed Acyclic Graph):有向无环图

6)二分图

无向图

所有的点划为左边一部分,右边一部分,保证图当中所有的边都是从左边的一个点连到右边的一个点

树一定是二分图


 

图的储存方法

邻接矩阵   ----------      边表

优点:O(1)检查  ||  O(m)的内存

缺点:内存大,重边只能记一条  ||  检查


 

最短路

单源最短路问题求一点到所有点的最短路

多源最短路问题求多点到所有点的最短路

最短路中的三角不等式:

dis[i][k]+dis[k][j]>=dis[i][j]

松弛操作

求最短路过程中

if  dis[i][k]+k->j(k到j的长度)<dis[i][j]
  dis[i][j]==dis[i][k]+k->j

 通过一条边来更新最短路的操作叫做松弛操作

最短路问题

1)Floyd

多源最短路算法

求任意连点之间的最短路

时间复杂度O(n3) || 空间复杂度O(n2)

处理范围n<=200~500

2)Dijkstra

单元最短路算法(前提:边权>=0)

求一个点到其他点的最短路

dis 1 2 3 4
初始化 0

 

(1-->1的最短路是0)

 

3)Dijkstra+Heap

 1 //By:Komorebi_03
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 const int N = 1e6+5;
 5 const int INF = 0x7fffffff;
 6 int n,m,s,a,b,c,cnt,head[N];
 7 long long ans[N];
 8 bool vis[N];
 9 struct Edge{
10     int nxt;
11     int v;
12     int w;
13 }e[N];
14 priority_queue<pair<int,int>> q;
15 inline int read()
16 {
17     int x=0,f=1;char ch=getchar();
18     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
19     while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
20     return x*f;
21 }
22 
23 void add(int u,int v,int w)
24 {
25     e[++cnt].v=v;
26     e[cnt].w=w;
27     e[cnt].nxt=head[u];
28     head[u]=cnt;
29 }
30 
31 void DJ(int u)
32 {
33     fill(ans,ans+N,INF);
34     q.push(make_pair(0,u));
35     ans[u]=0;
36     while (!q.empty())
37     {
38         int tmp=q.top().second;
39         q.pop();
40         if(vis[tmp])
41             continue;
42         vis[tmp]=1;
43         for (int i=head[tmp];i;i=e[i].nxt)
44         {
45             if(ans[e[i].v]>ans[tmp]+e[i].w)
46             {
47                 ans[e[i].v]=ans[tmp]+e[i].w;
48                 q.push(make_pair(-ans[e[i].v],e[i].v));
49             }
50         }
51     }
52 }
53 
54 int main()
55 {
56     n=read();
57     m=read();
58     s=read();
59     for (int i=1;i<=m;i++)
60     {
61         a=read();
62         b=read();
63         c=read();
64         add(a,b,c);
65     }
66     DJ(s);
67     for (int i=1;i<=n;i++)
68         cout<<ans[i]<<' ';
69     return 0;
70 }
DJ堆优化

4)Bellman-Ford

5)SPFA(4的一种优化

维护一个队列(?,队列里面的元素:可能改变其他点最短路的点

最坏情况下是O(nm),随机情况是O(km) k非常小的常数

负边权的题目,SPFA是你的唯一选择——zhx(((

 1 //By:Komorebi_03
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 #define int long long
 5 const int INF = 2147483647;
 6 const int N = 1e6+5;
 7 int n,m,s,e_cnt,dis[N],vis[N],head[N];
 8 struct node{
 9     int v;
10     int w;
11     int nxt;
12 }e[N];
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
17     while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
18     return x*f;
19 }
20 
21 void add(int u,int v,int w)
22 {
23     e[++e_cnt].v=v;
24     e[e_cnt].w=w;
25     e[e_cnt].nxt=head[u];
26     head[u]=e_cnt;
27 }
28 
29 void SPFA()
30 {
31     queue<int> q;
32     for (int i=1;i<=n;i++)
33     {
34         dis[i]=INF;
35         vis[i]=0;
36     }
37     q.push(s);
38     dis[s]=0;
39     vis[s]=1;
40     while(!q.empty())
41     {
42         int u=q.front();
43         q.pop();
44         vis[u]=0;
45         for (int i=head[u];i;i=e[i].nxt)
46         {
47             int v=e[i].v;
48             if(dis[v]>dis[u]+e[i].w)
49             {
50                 dis[v]=dis[u]+e[i].w;
51                 if(vis[v]==0)
52                 {
53                     vis[v]=1;
54                     q.push(v);
55                 }
56             }
57         }
58     }
59 }
60 
61 signed main()
62 {
63     n=read();
64     m=read();
65     s=read();
66     for (int i=1;i<=m;i++)
67     {
68         int u=read();
69         int v=read();
70         int w=read();
71         add(u,v,w);
72     }
73     SPFA();
74     for (int i=1;i<=n;i++)
75     {
76         if(s==i)
77             cout<<0<<" ";
78         else
79             cout<<dis[i]<<" ";
80     }
81     return 0;
82 }
SPFA

 例题:【APIO2013】TASKSAUTHOR - 题目 - Universal Online Judge (uoj.ac)

负环判定

1)最短路经过边数不能大于n-1

2)SPFA任何一个点入队次数不能超过n次

差分约束///

给定n个变量和m个不等式,xi-xj<=ak,求xn-x1的最大值

生成树

最小生成树,选出的n-1条边边权之和最小

1)Prim

2)Kruskal

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e6+5;
 4 int n,m,x,y,z,cnt,sum,ans,fa[N];
 5 bool f=false;
 6 struct node{
 7     int u;
 8     int v;
 9     int w;
10 }edge[N];
11 
12 bool cmp(node a,node b)
13 {
14     return a.w<b.w;
15 }
16 
17 void add(int u,int v,int w)
18 {
19     edge[++cnt].u=u;
20     edge[cnt].v=v;
21     edge[cnt].w=w;
22 }
23 
24 int Find(int x)
25 {
26     if(fa[x]==x)
27         return x;
28     return fa[x]=Find(fa[x]);
29 }
30 
31 void Kru()
32 {
33     sort(edge+1,edge+1+cnt,cmp);
34     for (int i=1;i<=cnt;i++)
35     {
36         int u=edge[i].u;
37         int v=edge[i].v;
38         int w=edge[i].w;
39         int r1=Find(u);
40         int r2=Find(v);
41         if(r1!=r2)
42         {
43             fa[r2]=r1;
44             sum++;
45             ans+=w;
46         }
47         if(sum==n-1)
48         {
49             f=true;
50             break;
51         }
52     }
53 }
54 
55 int main()
56 {
57     scanf("%d%d",&n,&m);
58     for (int i=1;i<=n;i++)
59         fa[i]=i;
60     for (int i=1;i<=m;i++)
61     {
62         scanf("%d%d%d",&x,&y,&z);
63         add(x,y,z);
64     }
65     Kru();
66     if(f)
67         cout<<ans<<'\n';
68     else
69         cout<<"orz"<<'\n';
70     return 0;
71 }
kruskal

强连通分量

有向图 G 强连通是指,G 中任意两个结点连通。

1)Tarjan

2)Kosaraju

3)Garbow

2-SAT

n个变量,每个变量只有两种取值(true || false)

m个表达式:xa op xb == c

问是否存一组解x1,x2,x3 . . .xn使得所有表达式成立

常用方法:Tarjan SCC 缩点

posted @ 2023-04-22 10:55  Komorebi_03  阅读(53)  评论(4编辑  收藏  举报