20200912 day7 图论复习(一)
1 图论基础
- 有向图\(G\)中,\(V\)表示顶点,\(E\)表示边。
- 无向图中不允许存在自环。
- 无向图中,定点的度是指关联于该顶点的边的数目。
- 有向图中,顶点的出度是离开该定点的边的数目。出度是指进入该顶点的边的数目。度是入度与出度之和。
1.1 邻接矩阵
\(|E|=n\)的邻接矩阵是一个$n \times 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\%\)的数据,\(N\leq 10\),\(M\leq 20\);
对于\(70\%\)的数据,\(N\leq 100\),\(M\leq 1000\);
对于\(100\%\)的数据,\(N\leq 10000\),\(M\leq 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\n");
}
else{
printf("N\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;
}
要做就做南波万