文章目录
一、图论是什么
百度百科:图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。
链接:图论
在我的理解里,图论就是研究各种图,将他们存储起来,用算法解决图中的问题,如最短路等。
二、图论中的基本概念
图论中涉及许多概念,包括图的基本结构:节点和边,图的类型:有向图和无向图等等。我们一般用节点和边的集合来表示一个图。
图论的基本概念有很多,这里便不一一介绍,详细请看图论基本概念
三、图的储存
1.邻接矩阵
最简单粗暴的储存方式,可以vector数组或者直接用二维数组。map[i][j]表示i到j这条边的权值,如为0则为不通。
优点:容易理解,容易使用。
缺点:使用的时候时间复杂度高,空间复制度高
2.邻接链表
• 对于每个点vi开头指针h[i]=NULL,表示一条起始点为vi的边的位置。
• 例如:h[1]=2’->3’->4’->NULL,表示三条边,分别是1->2 ,1->3 ,1->4
• X’表示边的编号,即下面的cnt
• 对于边,用edge{ to ,next }的结构体存放,to表示这条边的结束点,next
表示下一条边。
• 那么对于加u->v边操作,edge[++cnt].to=v; .next=h[u]; h[u]=cnt;
3.链式前向星
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 100;
const int MAXM = 100;
int head[MAXN];
struct EdgeNode
{
int to;
int w;
int next;
};
EdgeNode Edges[MAXM];
int main()
{
int n,m,x,y,w;
cin >> n >> m;
memset(head,-1,sizeof(head));
for(int i = 0; i < m; i++)
{
cin >> x >> y >> w;
Edges[i].to = y;
Edges[i].w = w;
Edges[i].next = head[x];
head[x] = i;
}
cout << endl;
for(int i = 1; i <= n; i++)
{
for(int k = head[i]; k!= -1; k = Edges[k].next)
cout << i << ' ' << Edges[k].to << ' ' << Edges[k].w << endl;
}
return 0;
}
我个人认为邻接链表应该是比较好的一种…
四、最短路径问题
1.Bellman-Ford算法(求单源最短路径)
用d[i]=min(d[j]+j到i的边的权值)不断地去递推更新d的值
2.Dijkstra算法(求单源最短路径)
个人理解为Ford的改进,但不能用为有负边的情况
3.Floyd算法(求各顶点之间最短路径)
用dp的思想,dp[i][j]记录i到j的最短路径
通过三层循环最终推出dp中的所有值
缺点:时间复杂度过高,为v的三次方(v为顶点个数)
五、并查集
板子题:【模板】并查集
可以用于最小生成树问题
#include <iostream>
using namespace std;
int father[10010];
void creat(int n)
{
for (int i=1;i<=n;i++)
father[i]=i;
}
int findd(int x)
{
while (father[x]!=x)
x=father[x]=father[father[x]];
return x;
}
int main()
{
int n,m;
cin>>n>>m;
creat(n);
while (m--)
{
int z,x,y;
cin>>z>>x>>y;
int a=findd(x),b=findd(y);
if (z==1)
{
father[a]=b;
}else
{
if (a==b)
cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
}
return 0;
}
六、本专题题解
1.Cow Contest
思路:因数据很小,可以利用Floyd去计算各个顶点直接是否相连,然后计算各个顶点分别与多少个顶点相连。
#include <iostream>
using namespace std;
int main()
{
int n,m,i,j,k,ans=0;
cin>>n>>m;
int dp[110][110]= {};
for (i=0; i<m; i++)
{
int a,b;
cin>>a>>b;
dp[a][b]=1;
}
for (k=1; k<=n; k++)
for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
if (dp[i][k]&&dp[k][j])
dp[i][j]=1;
for (i=1; i<=n; i++)
{
int t=0;
for (j=1; j<=n; j++)
if (dp[i][j])
t++;
for (j=1; j<=n; j++)
if (dp[j][i])
t++;
if (t==n-1)
ans++;
}
cout<<ans<<endl;
return 0;
}
2.最短路
思路:板子题,因为数据小,所以可以用Floyd求出。
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
while (1)
{
int n,m,i,k,j;
cin>>n>>m;
int f[110][110]={};
memset(f,0x3f,sizeof(f));
if (n==0&&m==0)
break;
for (i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
f[a][b]=c;
f[b][a]=c;
}
for (k=0;k<n;k++)
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
cout<<f[1][n]<<endl;
}
return 0;
}
3.六度分离
思路:用Floyd求出各个顶点之间的距离,看有没有超过7的
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int n,m;
while (cin>>n>>m)
{
int i,j,k;
int dp[110][110]= {};
memset(dp,0x3f,sizeof(dp));
for (i=0; i<m; i++)
{
int a,b;
cin>>a>>b;
dp[a][b]=1;
dp[b][a]=1;
}
for (k=0; k<n; k++)
for (i=0; i<n; i++)
for (j=0; j<n; j++)
// if (dp[i][k]&&dp[k][j])
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
int p=1;
for (i=0; i<n; i++)
{
for (j=i+1; j<n; j++)
if (dp[i][j]>7)
p=0;
}
if (p)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}