树上问题
图的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)