首页 > 其他分享 >图论基础

图论基础

时间:2023-09-02 16:46:17浏览次数:39  
标签:图论 int 基础 head edge maxn low include

图的存储

图的存储:B3643

AC Code:

#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=1005;
// 邻接矩阵
ll a[maxn][maxn];
// 邻接表
vector<ll> adj_list[maxn];
ll n,m;
int main(){
    cin>>n>>m;
    // 读取边的信息并构建邻接矩阵和邻接表
    for(ll i=1;i<=m;i++){
        ll u,v;
        cin>>u>>v;
        a[u][v]=1;
		a[v][u]=1; // 无向图,所以两个方向都要标记为1
        adj_list[u].push_back(v);
        adj_list[v].push_back(u);
    }
    // 输出邻接矩阵
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++){
            cout<<a[i][j]<<" ";
        }
        cout<<endl;
    }
    // 输出邻接表
    for(ll i=1;i<=n;i++){
    	sort(adj_list[i].begin(),adj_list[i].end());
        cout<<adj_list[i].size()<< " ";
        for(ll j=0;j<adj_list[i].size();j++){
            cout<<adj_list[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

链式前向星存图

Code:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
#define ll long long
const int maxn=1e5+5;
struct edge{
	int to;//边的目标节点 
	int w;//边的权重 
	int next;//下一条边 
};
vector<edge> edges;//存储所有边
int head[maxn];//存储每个节点的第一条边
int edge_num;//边的数量
void addedge(int u,int v,int w){
	edges.push_back({u,head[u],w});
	//有向图 
	//将一条从u到v的边1添加到图中,将head[u]中原本存储的边的索引作为新边的next 
	head[u]=edge_num++;
	//将新边的索引存储到head[u]中 
} 
int n,m,u,v,w;
int main(){
    cin>>n>>m;
	memset(head,-1,sizeof(head));//初始化,每个节点暂时没有边 
	for(int i=1;i<n;i++){
		cin>>u>>v>>w;
		addedge(u,v,w);
	}
    return 0;
}

图的遍历

dfs遍历

P3916 图的遍历

正向存:

由于数据范围过大,为了节约时间反向存图

反向存:

AC Code:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
int n,m,u,v;
struct node{
	int to;
	int next;
};
vector<node> edge;
int head[maxn];
int edgenum;
void add(int u,int v){
	edge.push_back({v,head[u]});
	head[u]=edgenum++;
}
int ans[maxn];
void dfs(int x,int maxx){
	if(ans[x]!=0)return ;
	ans[x]=maxx;
	for(int i=head[x];i!=-1;i=edge[i].next){
		int v1=edge[i].to;
		dfs(v1,maxx);
	}
}
int main(){
    cin>>n>>m;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++){
    	cin>>u>>v;
    	add(v,u);
	}
	for(int i=n;i>=1;i--){
		dfs(i,i);
	}
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<" ";
	}
    return 0;
}

P2853

AC Code(dfs):

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=10005;
int k,n,m,u,v;
struct node{
	int to;
	int next;
};
vector<node> edge;
int head[maxn];
int edgenum;
/*
必须设为0而不是1 
C++中数组和vector的下标都是从0开始 
如果这里设为1,那么head[u]实际上存储的是第二条边 
*/ 
int ans;
int a[maxn];
void add(int u,int v){
	edge.push_back({v,head[u]});
	head[u]=edgenum++;
}
int can[maxn];
bool vis[maxn];
void dfs(int x){
	vis[x]=1;
	//防止重复搜索从而爆栈,MLE 
	can[x]++;
	for(int i=head[x];i!=-1;i=edge[i].next){
		int x1=edge[i].to;
		if(vis[x1]==0)dfs(x1);
	}
}
int main(){
    cin>>k>>n>>m;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=k;i++){
    	cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		add(u,v);
	}
	for(int i=1;i<=k;i++){
		dfs(a[i]);
		memset(vis,0,sizeof(vis)); 
		//切换牧场时初始化标记数组 
	}
	for(int i=1;i<=n;i++){
		if(can[i]==k)ans++;
	} 
	cout<<ans;
    return 0;
}

bfs遍历

P2853

AC Code(bfs):

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=10005;
int k,n,m,u,v;
struct node{
	int to;
	int next;
};
vector<node> edge;
int head[maxn];
int edgenum;
/*
必须设为0而不是1 
C++中数组和vector的下标都是从0开始 
如果这里设为1,那么head[u]实际上存储的是第二条边 
*/ 
int ans;
int a[maxn];
void add(int u,int v){
	edge.push_back({v,head[u]});
	head[u]=edgenum++;
}
int can[maxn];
bool vis[maxn];
void bfs(int x){
	queue<int> q;
	vis[x]=1;
	q.push(x);
	while(!q.empty()){
		int cur=q.front();
		q.pop();
		can[cur]++;
		for(int i=head[cur];i!=-1;i=edge[i].next){
			int x1=edge[i].to;
			if(vis[x1]==0){
				vis[x1]=1;
				q.push(x1);
			}
		}
	}
}
int main(){
    cin>>k>>n>>m;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=k;i++){
    	cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		add(u,v);
	}
	for(int i=1;i<=k;i++){
		bfs(a[i]);
		memset(vis,0,sizeof(vis)); 
	}
	for(int i=1;i<=n;i++){
		if(can[i]==k)ans++;
	} 
	cout<<ans;
    return 0;
}

综合

P5318

排序过程:把

排序成

这个样子就可以了

也就是只排序同一层的文献

AC Code:

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#include<algorithm>

using namespace std;

const int maxn=1e6+5;

int n,m,x,y;

struct node{
	int to;
	int next;
};
vector<node> edge1;
vector<node> edge;
int head[maxn];
int edge_num;

bool cmp(node x,node y){//注意判定条件!!! 
	if(x.to==y.to)return x.next>y.next;
	return x.to<y.to;
}//从大到小录入,只需排序同一层文献,文献层次保持不变! 

void add(int u,int v){
	edge.push_back({v,head[u]});
	head[u]=edge_num++;
}

int vis_dfs[maxn];
void dfs(int x){
	vis_dfs[x]=1;
	for(int i=head[x];i!=-1;i=edge[i].next){
		int x1=edge[i].to;
		if(vis_dfs[x1]==0){
			cout<<x1<<" ";
			dfs(x1);
		}
	}
}

int vis_bfs[maxn];
void bfs(int x){
	vis_bfs[x]=1;
	queue<int> q;
	q.push(x);
	while(!q.empty()){
		int cur=q.front();
		q.pop();
		for(int i=head[cur];i!=-1;i=edge[i].next){
			int cur1=edge[i].to;
			if(vis_bfs[cur1]==0){
				cout<<cur1<<" ";
				vis_bfs[cur1]=1;
				q.push(cur1);
			}
		}
	}
}

int main(){
    
    cin>>n>>m;
    
    memset(head,-1,sizeof(head));
    
    for(int i=1;i<=m;i++){
    	cin>>x>>y;
    	edge1.push_back({x,y});
    	add(x,y);
	}
	
	sort(edge1.begin(),edge1.end(),cmp);
	
	for(int i=1;i<=m;i++){
    	add(edge1[i].to,edge1[i].next);
	}
    
    cout<<"1 ";
	dfs(1);
	
	cout<<endl<<"1 ";
    bfs(1);
    
	return 0;
}

图的连通性

前置知识:时间戳和追溯点

时间戳:dfn[u]表示节点u深度优先遍历的序号。

追溯点:low[u]表示节点u或u的子孙能通过非父子边追溯到的dfn最小值,即回到最早的过去。

Tarjan算法

用于求解桥和割点

桥判定法则:无向边x-y是桥,当且仅当存在x的一个子节点y时,满足low[y]>dfn[x]。

也就是说,若孩子的low值比自己的dfn值大,说明孩子回不到起点,则从该节点到这个孩子的边为桥。

Code:

#include<iostream>
#include<vector>
#include<cstring> 

using namespace std;

const int maxn=1e5+5;

int n,m,u,v,root;//root为根节点 

struct node{
	int to;
	int next;
};

vector<node> edge;
int head[maxn];
int edgenum;

void add(int u,int v){
	edge.push_back({v,head[u]});
	head[u]=edgenum++;
}

int dfn[maxn],low[maxn],nodenum=1;//时间戳,追溯点,节点序号 

void tarjan_bridge(int u,int fa){//求桥 
	//从u出发,fa为u的父节点 
	dfn[u]=low[u]=nodenum++;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa){//如果v是u的父节点 
			continue; 
		}
		if(dfn[v]==0){//如果v点未赋值 
			tarjan_bridge(v,u);//访问v,u为v的父节点 
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){//判断是否符合条件 
				cout<<u<<"-"<<v<<"是桥"<<endl; 
			}
		}else{//如果赋了值,那么更新low并返回 
			low[u]=min(low[u],dfn[v]);
		}
	} 
} 


int main(){
	
	while(cin>>n>>m){
		
		memset(head,-1,sizeof(head));
		
		while(m--){
			cin>>u>>v;
			add(u,v);
			add(v,u);
		}
		
		for(int i=1;i<=n;i++){//可能有多个桥,依次访问每个节点 
			if(dfn[i]==0){//如果没访问过 
				tarjan_bridge(i,0);
			}	
		}
			
	}
	
    return 0;
}

割点判定法则:

若x不是根节点,则x是割点,当且仅当存在x的一个子节点y,满足low[y]>=dfn[x];

若x是根节点,则x是割点,当且仅当至少存在两个子节点,满足该条件。

也就是说,如果不是根,且孩子的low值大于或等于自己的dfn值,则该节点就是割点;

如果是根,则至少需要两个孩子满足条件。

Code:

#include<iostream>
#include<vector>
#include<cstring> 

using namespace std;

const int maxn=1e5+5;

int n,m,u,v,root;//root为根节点 

struct node{
	int to;
	int next;
};

vector<node> edge;
int head[maxn];
int edgenum;

void add(int u,int v){
	edge.push_back({v,head[u]});
	head[u]=edgenum++;
}

int dfn[maxn],low[maxn],nodenum=1;//时间戳,追溯点,节点序号 

void tarjan_cut(int u,int fa){//求割点 
	//从u出发,fa为u的父节点 
	dfn[u]=low[u]=nodenum++;
	int count=0;//记录有几个儿子满足条件 
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa){//如果v是u的父节点 
			continue;
		}
		if(dfn[v]==0){//如果v点未赋值 
			tarjan_cut(v,u);//访问v,u为v的父节点 
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){//判断是否符合条件 
				count++;//满足条件的儿子+1 
				if(u!=root||count>=2){
				//如果u不是树根 或者 是树根且有两个以上儿子满足要求 
					cout<<u<<"是割点"<<"\n";//u是割点 
				}
			}
		}else{//如果赋了值,那么更新low并返回 
			low[u]=min(low[u],dfn[v]);
		}
	} 
} 

int main(){
	
	while(cin>>n>>m){
		
		memset(head,-1,sizeof(head));
		
		while(m--){
			cin>>u>>v;
			add(u,v);
			add(v,u);
			//无向图 
		}
		
		for(int i=1;i<=n;i++){
			//有可能是非连通图,需要从每个节点开始检查 
			if(dfn[i]==0){//如果没访问过 
				root=i;//记录起点作为树根
				tarjan_cut(i,0); 
			}
		}
		
	}
	
    return 0;
}

标签:图论,int,基础,head,edge,maxn,low,include
From: https://www.cnblogs.com/hnzzlxs01/p/17673868.html

相关文章

  • python基础语法之字符串
    字符串扩展1、字符串的三种定义方式单引号,双引号,三引号a='abc';b="sdf";c='''ewrc''';print(a,b,c);2、字符串的拼接#字符串字面量之间的拼接print("我是一名"+"大学生"+","+"学习智能医学工程");#字符串字面量和字符串变量的拼接name='......
  • 【WCH蓝牙系列芯片】-基于CH582开发板—基础外设输出PWM波形讲解
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------在WCH官方提供的CH583的EVT资源包中,我们可以找到PWMX的例程,这是一个8位的PWM输出,占空比和周期可调的......
  • 基础linux命令
    前言:由于在实际开发过程中服务器大多部署在linux系统下,所以特此来学习linux的基本操作1.1pwdpwd命令的目的是打印当前目录,告诉你目前在哪里比如我在kali终端中输入pwd,实际返回为/home/kali1.2lsls可以列出当前目录下有什么文件当然也可以查看不同目录的内容,比如ls文件地......
  • JSTL基础部分
    在使用JSTL时记得正确引入了JSTL标签库<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%>jstlif标签判断test属性表示判断的条件(使用EL表达式输出)<br><c:iftest="${12==12}">正确<br></c:if>jstl多路判断<......
  • 关于SpringBoot中引入html模板的问题的解决(基础)
    问题描述将相关的文件放置到resources/static文件夹目录下面,文件路径正确,但是一直应用不了问题解决原来是在引用的时候,需要在每个文件前面加上一个斜杠——/,这样就解决啦!......
  • ps基础配置
    认识ps简单设置首先进入ps  1、代表菜单栏2、代表属性栏3、代表工具栏4.、代表工作区5、代表调整面板首选项设置:CTRL+K(快捷键)   第二种方法:在菜单栏找到编辑-首选项-常规 文件处理:自动存储恢复信息的间隔 设置为5分钟  性能:内存使用情况:蓝色色条不要超过80......
  • java基础-流程控制-day04
    目录1.if单分支2.ifelse多分支3.ifelse双分支4.随机生成一定区间的整数5switch1.if单分支publicclassTestIf01{ publicstaticvoidmain(String[]args){ //对三个数(1-6)求和 intnum1=6; intnum2=6; intnum3=5; intsum=0; sum+=nu......
  • 使用AI写邮件-AI基础系列文章第18篇
    您的关注是对我最大的支持......
  • Markdown基础语法学习,学习写博客的第一步
    Markdown学习1.标题开头"#"+...:一级标题有n个#表示n级标题2.字体(1)星号:我向往自由,我要谈恋爱!我向往自由,我要谈恋爱!我向往自由,我要谈恋爱!其中"两个星号"+...+"两个星号"表示粗体一个星号表示斜体,三个星号表示斜体加粗体(2)波浪号:我向往自由,我要谈恋爱!我向往自由,......
  • 7基础扩展
    磁盘阵列RAIDRaid0 条块化:性能最高,并行处理,无冗余,损坏无法恢复Raid1镜像结构:可用性,可修复性,仅有50%利用率Raid0+1Raid10:radio与raid1长处结合,高效也可靠Raid3(奇偶校验并行传送:N+1模式 有固定的校验盘坏一个盘可恢复Raid5(分布式奇偶校验的独立磁盘):N+1模式,无固定......