图论

本文详细介绍了图论中的树上问题,包括树的重心、单源最短路算法如Dijkstra、Bellman-Ford和SPFA,拓扑排序的应用,以及二分图的概念及其最大匹配、最小点覆盖等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树上问题

图的dfs和bfs

#include<bits/stdc++.h>
using namespace std;
const int N=100001;
struct edge{
   
	int u,v;
};
vector<int> e[N];
vector<edge> s;//存边的 
bool cmp(edge x,edge y)
{
   
	if( x.v==y.v) return x.u<y.u;
	else return x.v<y.v;
}
bool vis1[N],vis2[N];

void dfs(int x){
   
	vis1[x]=1;
	cout<<x<<" ";
	for(int i=0;i<e[x].size();i++){
    //一条一条边去搜索
		int point=s[e[x][i]].v;  //找出当前这条边(也就是e[x][i])的终点
		if(!vis1[point]){
      //如果这个点没走过
			dfs(point); //接着往下搜
		}
	}
} 
void bfs(int x){
      //广度优先遍历
	queue <int> q;  
	q.push(x);  //先把第一个顶点推进去,输出,标记访问过
	cout<<x<<" ";  
	vis2[x]=1; 
	while(!q.empty()){
      
		int fro=q.front();  //把队首取出来
		for(int i=0;i<e[fro].size();i++){
      //每条边去试
			int point=s[e[fro][i]].v;  //取终点(和dfs差不多)
			if(!vis2[point]){
     //没访问过,推进去,输出,标记
				q.push(point); 
				cout<<point<<" ";
				vis2[point]=1;
			}
		}
		q.pop();  //把队首弄出去
	}
}
int main(){
   
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
   
		int uu,vv;
		cin>>uu>>vv;
		s.push_back((edge){
   uu,vv});//初始化s 	
	}
	sort(s.begin(),s.end(),cmp);
	for(int i=0;i<m;i++) e[s[i].u].push_back(i);//初始化e 
	dfs(1);
	cout<<endl;
	bfs(1);
	return 0; 
} 

树的重心

定义

在一棵树中,若有一个点,除去它之后,树变成了两个连通块,这两个连通块的规模尽可能地接近,那么这个点就是这棵树的重心。
很显然,每棵树都有至少一个重心。

求树的重心

采用一遍dfs,计算每个点的相连的最大的连通块规模,求取最大值;再在所有最大值中找出最小的,这个点就是重心。
f[i]代表第i个点相连的几个连通块中较大者的规模。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
   
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}

    const int maxn=103000;
    int n;
    struct node
    {
   
        int y,next;
    }a[maxn*2];
    int head[maxn],top;
    //邻接表
    void add(int x,int y)
    {
   
        top++;
        a[top].y=y;
        a[top].next=head[x];
        head[x]=top;
    }
    //连一条从x到y的边
    void init()
    {
   
        n=read();
        for(int i=1;i<n;i++)
        {
   
            int x=read();
            int y=read();
            add(x,y);
            add(y,x);
        }
    }
    //读入树的有关内容
    const int INF=2e9;
    int minn=INF;
    int f[maxn],size[maxn];
    void dfs(int u,int fa)
    {
   
        size[u]=1;//默认就只包含自己
        for(int i=head[u];i;i=a[i].next)
        {
   
            int v=a[i].y;
            if(v==fa)continue;
            dfs(v,u);
            size[u]+=size[v];
            f[u]=max(f[u],size[v]);
            //找出u所有子树中规模最大的
        }
        f[u]=max(f[u],n-size[u]);
        //规模n-size[u]是除去v的子树和u本身剩下的部分
        //也就是另一个连通块
        minn=min(minn,f[u]);
    }

int main()
{
   
    init();
    dfs(1,0);
    for(int i=1;i<=n;i++)
        if(f[i]==minn)
            printf("%d ",i);
    return 0;
}




邻接表建图

#include<bits/stdc++.h>
using namespace std;
const int maxn=103000;
int n;
struct node
{
   
    int y,next;
}a[maxn*2];
queue<int > q;

inline int read()
{
   
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}


int head[maxn],top;
    //邻接表
void add(int x,int y)
{
   
    top++;
    a[top].y=y;
    a[top].next=head[x];
    head[x]=top;
}
    //连一条从x到y的边

const int INF=2e9;
int minn=INF;
int f[maxn],size[maxn];
void dfs(int u,int fa)
{
   
    size[u]=1;//默认就只包含自己
    for(int i=head[u];i;i=a[i].next)
        {
   
            int v=a[i].y;
            if(v==fa)continue;
            dfs(v,u);
            size[u]+=size[v];
            f[u]=max(f[u],size[v]);
            //找出u所有子树中规模最大的
        }
        f[u]=max(f[u],n-size[u]);
        //规模n-size[u]是除去v的子树和u本身剩下的部分
        //也就是另一个连通块
        minn=min(minn,f[u]);
}
int t[maxn];
int ans=0,sum=0;
void bfs(){
   
	q.push(ans);
	while(!q.empty()){
   
		int xx=q.front();
		q.pop();
		for(int i=head[xx];i;i=a[i].next){
   
			int yy=a[i].y;
			if(t[yy]||yy==ans) continue;
			t[yy]=t[xx]+1;
			sum+=t[yy];
			q.push(yy); 
		}
	}
	return ;
}
int main()
{
   
    n=read();
    for(int i=1;i<n;i++)
    {
   
        int x=read();
        int y=read();
        add(x,y);
        add(y,x);
    }
    
    dfs(1,0);
    for(int i=1;i<=n;i++)
        if(f[i]==minn){
   
        	ans=i;
        	break;
        }
    bfs();
	
	printf("%d %d",ans,sum);
	return 0;
}

洛谷p1395 会议

在这里插入图片描述
这道题目实际上就是求树的重心,那么我们的思路就转化为:在 O(n)内先求出树的重心,再用 dfs 求出到重心的距离之和。

在这里插入图片描述
vector数组建图

#include<bits/stdc++.h> 
using namespace std;  //标准开头
const int N=50010;  //节点数
int n,ans;  //n——节点数,ans为树的重心
int maxn=10000000;

int u,r,sum;  //u,r边的两端点,sum——距离之和
int d[N],dis[N];  //dis[i]为节点i到源点(树的重心)的距离
vector<int> G[N];  //vector建图
queue<int> q;  //BFS必备队列
bool vis[N];  //BFS中已经遍历的点

void dfs(int s,int f){
     //求树的重心
    d[s]=1;
    int res=0;
    for(int i=0;i<G[s].size();i++){
   
        if(G[s][i]==f) continue;
        dfs(G[s][i],s);
        d[s]+=d[G[s][i]];
        res=max(res,d[G[s][i]]);
    }
    res=max(res,n-d[s]);
    if(res<maxn||(res==maxn&&ans>s)){
   
        maxn=res;
        ans=s;
    }
}
int main()
{
   
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
   
        scanf("%d%d",&u,&r);
        G[u].push_back(r);
        G[r].push_back(u);
    }
    dfs(1,0);
    q.push(ans);
    while(!q.empty()){
     //标准BFS
        int e=q.front();
        q.pop();
        vis[e]=true;
        sum+=dis[e];
        for(int i=0;i<G[e].size();i++){
   
            if(!vis[G[e][i]]){
   
                q.push(G[e][i]);
                dis[G[e][i]]=dis[e]+1;
            }
        }
    }
    printf("%d %d",ans,sum);  //完美输出
    return 0;
}

单源最短路

Dijkstra

#include<bits/stdc++.h>
using namespace std;

const int N=10010,M=1000010;
int head[N],ver[M],edge[M],Next[M],d[N];
bool v[N];
int n,m,tot;

priority_queue<pair<int ,int> > q;

void add(int x,int y, int z)
{
   
	ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
} 

void dijkstra()
{
   
	memset(d,0x3f,sizeof d);
	memset(v,0,sizeof v);
	d[1]=0;//起点 
	q.push(make_pair(0,1));
	while(q.size()){
   
		int x=q.top().second;q.pop();
		if(v[x]) continue;
		v[x]=1;
		for(int i=head[x];i;i=Next[i]){
   
			int y=ver[i],z=edge[i];
			if(d[y]>d[x]+z){
   
				d[y]=d[x]+z;
				q.push(make_pair(-d[y],y));
			}
		}
	}
}
int main()
{
   
	cin>>n>>m;
	for(int i=1;i<=m;i++){
   
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	dijkstra();
	for(int i=1;i<=n;i++) printf("%d\n",d[i]);
	return 0;
}

bellman_ford

在这里插入图片描述

#include<bits/stdc+.h>
using namespace std;

const int N=310;
int d[310][310],n,m;

int main(){
   
	cin>>n>>m;
	memset(d,0x3f,sizeof(d));
	for(int i=1;i<=n;i++) d[i][i]=0;
	for(int i=1;i<=n;i++){
   
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		d[x][y]=min(d[x][y],z);
	}
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
	for(int i=1;i<=n;i++){
   
		for(int j=1;j<=n;j++) printf("%d ",d[i][j]);
		puts("");
	} 
	 
} 

SPFA

蓝书spfa

#include<bits/stdc++.h>
using namespace std;

const int N=100010,M=1000010;
int head[N],ver[M],edge[M],Next[M],d[N];
bool v[N];
int n,m,tot;

queue<int> q;

void add(int x,int y,int z)
{
   
	ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
 } 
 
void spfa(){
   
	memset(d,0x3f,sizeof(d) );
	memset(v,0,sizeof(v) );
	d[1]=0;v[1]=1;
	q.push(1);
	while(q.size())
	{
   
		int x=q.front();q.pop();
		v[x]=0;
		for(int i=head[x];i;i=Next[i]){
   
			int y=ver[i],z=edge[i];
			if(d[y]>d[x]+z){
   
				d[y]=d[x]+z;
				if(!v[y]) q.push(y),v[y]=1;
			}		
		}
	}
}
int main()
{
   
	cin>>n>>m;
	for(int i=1;i<=m;i++){
   
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	spfa();
	for(int i=1;i<=n;i++) printf("%d\n",d[i]);
	return 0;
}

静态邻接表

//边数m<=500000,邻接矩阵存不下,只能使用静态邻接表存储
#include<bits/stdc++.h>
const long long inf=2147483647;
const int maxn=10005;
const int maxm=500005;
using namespace std;
int n,m,s,num_edge=0;
int dis[maxn],vis[maxn],head[maxm];
struct Edge
{
   
  int next,to,dis;
}edge[maxm]; //结构体表示静态邻接表
void addedge(int from,int to,int dis) //邻接表建图
{
    //以下是数据结构书上的标准代码,不懂翻书看解释
  edge[++num_edge].next=head[from]; //链式存储下一条出边
  edge[num_edge].to=to; //当前节点编号
  edge[num_edge].dis=dis; //本条边的距离
  head[from]=num_edge; //记录下一次的出边情况
}
void spfa()
{
   
  queue<int> q; //spfa用队列,这里用了STL的标准队列
  for(int i=1; i<=n; i++) 
  {
   
    dis[i]=inf; //带权图初始化
    vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组
  }
  q.push(s); dis[s]=0; vis[s]=1; //第一个顶点入队,进行标记
  while(!q.empty())
  {
   
    int u=q.front(); //取出队首
    q.pop(); vis[u]=0; //出队标记
    for(int i=head[u]; i; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替)
    {
   
      int v=edge[i].to; 
      if(dis[v]>dis[u]+edge[i].dis) //如果有最短路就更改
      {
   
        dis[v]=dis[u]+edge[i].dis;
        if(vis[v]==0) 
1. 内容概要 本项目是一个支持科学函数的命令行计算器,兼容 C++98 标准。它实现了中缀表达式词法分析、后缀表达式转换与求值,支持常见数学运算(如幂、三角函数、对数等)与括号优先级解析。程序还提供了角度版三角函数、角度与弧度互转功能,并支持函数调试输出与函数演示模式。 2. 适用人群 * C++ 初中级学习者,特别是希望深入理解表达式求值机制者 * 需要一个可扩展的计算引擎的项目开发者 * 想通过项目实践词法分析、调度场算法、数学函数封装的开发者 * 高校学生课程设计、编译原理实践者 3. 使用场景及目标 * 实现中缀表达式的完整求解器,支持函数嵌套、优先级与结合性处理 * 提供角度与弧度版本的三角函数,以适应不同输入偏好 * 演示中缀转后缀过程,辅助编程教育与算法教学 * 提供科学函数辅助计算,如 `log`, `sqrt`, `abs`, `exp`, `ceil`, `floor` 等 4. 其他说明 * 支持函数:sin, cos, tan(弧度);sind, cosd, tand(角度) * 支持函数嵌套,如 `sin(deg2rad(30))` * 支持操作符:+, -, \*, /, ^, \*\*(幂运算)与括号优先级 * 所有函数均通过 map 注册,方便扩展与自定义 * 输入 `help` 查看支持函数,`demo` 观看转后缀过程,`quit` 退出程序 * 提示用户避免使用 `°` 符号,推荐使用角度函数代替 * 可通过 `g++ calculator.cpp -o calculator -lm` 编译(需链接数学库)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值