1 图论基础
- 有向图(G)中,(V)表示顶点,(E)表示边。
- 无向图中不允许存在自环。
- 无向图中,定点的度是指关联于该顶点的边的数目。
- 有向图中,顶点的出度是离开该定点的边的数目。出度是指进入该顶点的边的数目。度是入度与出度之和。
1.1 邻接矩阵
(|E|=n)的邻接矩阵是一个$n imes n $的矩阵。无权图中用1表示邻接,0表示不邻接。
带权图中数字表示边的权重,无穷(有时用0)表示两点不邻接。
无向图的邻接矩阵是关于对角线对称的。
1.2 链式前向星
空间复杂度(O(E))
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 5005
#define M 20005
using namespace std;
int cnt=0,head[N];
struct node{
int to,next,val;
}edge[N];
void add(int x,int y,int val1){
edge[++cnt].next=head[x];
edge[cnt].to=y;
edge[cnt].val=val1;
head[x]=cnt;
}
int main(){
}
memset(head,-1,sizeof(head));
for(int i=head[x];i!=-1;i=edge[i].next){
int to1=edge[i].to;
//do something
}
2 最小生成树
2.0 并查集
模板 LGP3367
题目描述
现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数(N)、(M),表示共有(N)个元素和(M)个操作。
接下来(M)行,每行包含三个整数(Z_i)、(X_i)、(Y_i)
当(Z_i=1)时,将(X_i)与(Y_i)所在的集合合并
当(Z_i=2)时,输出(X_i)与(Y_i)是否在同一集合内,是的话输出(Y);否则话输出(N)
输出格式
如上,对于每一个(Z_i=2)的操作,都有一行输出,每行包含一个大写字母,为(Y)或者(N)
输入输出样例
输入
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
输出
N
Y
N
Y
数据规模
对于(30\%)的数据,(Nleq 10),(Mleq 20);
对于(70\%)的数据,(Nleq 100),(Mleq 1000);
对于(100\%)的数据,(Nleq 10000),(Mleq 200000)。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 50005
using namespace std;
//get together jihe
int fa[N];
int n;
void init(){
for(int i=1;i<=n;i++) fa[i]=i;
}
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void uni(int x,int y){
fa[find(x)]=find(y);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i;
//init();
while(m--){
int a=0,b=0,c=0;
scanf("%d%d%d",&a,&b,&c);
if(a==1){
fa[find(b)]=find(c);
}
else if(a==2){
if(find(b)==find(c)){
printf("Y
");
}
else{
printf("N
");
}
}
}
return 0;
}
2.1 Kruskal
//Kraskul
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 5005
#define M 200005
using namespace std;
int cnt=0,head[M],fa[M];//fa维护点的连通性
struct node{
int to,next,val;
}edge[M];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
bool cmp(node x,node y){
return x.val<y.val;
}
void add(int x,int y,int val1){
edge[++cnt].next=head[x];
edge[cnt].to=y;
edge[cnt].val=val1;
head[x]=cnt;
}
int tgn,tgto,ans;
int main(){
memset(head,-1,sizeof(head));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&edge[i].next,&edge[i].to,&edge[i].val);
}
//kruskal
sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=m;i++){
tgn=find(edge[i].next);
tgto=find(edge[i].to);
if(tgn==tgto){
continue;
}
//若已经联通,则不需要这一条边
ans+=edge[i].val;
//边权记录答案
fa[tgto]=tgn; //联通tgto和tgn
if(++cnt==n-1){
break;
}
//边为点数减一时停止
}
if(ans!=0) printf("%d",ans);
else printf("orz");
return 0;
}
/*
for(int i=head[x];i!=-1;i=edge[i].next){
int to1=edge[i].to;
//do something
}
*/
2.2 Prim
复杂度(O(V^2)),适用于稠密图
prim详解
3 最短路
3.1 单源最短路径
(弱化版)
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define N 20005
#define edge e
using namespace std;
struct node{
int to,next,val;
}e[1000001];
int head[N],num;
void add(int x,int y,int val1){
e[++num].next=head[x];
e[num].to=y;
e[num].val=val1;
head[x]=num;
}
bool vis[20005]={0};
long long dis[20005];
int n,m,s;
int a,b,c;
#define K 2147483647
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=n;i++) dis[i]=K;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
int curr=s;
dis[s]=0;
long long minn;
while(!vis[curr]){
vis[curr]=1;
for(int i=head[curr];i!=0;i=edge[i].next){
if(!vis[edge[i].to]&&dis[edge[i].to]>dis[curr]+edge[i].val){
dis[edge[i].to]=dis[curr]+edge[i].val;//更新每个点
}
}
minn=K;
for(int i=1;i<=n;i++){
if(!vis[i]&&minn>dis[i]){
minn=dis[i];
curr=i;
}
}
}
for(int i=1;i<=n;i++) printf("%lld ",dis[i]);
return 0;
}