教程1.存图建图
我们都知道,
图和树都由“点”和“边”组成
(一条边连接两个点,下文将一条边指向的点叫做”儿子点“,另一个点则称作“父亲点”)
所以我们只要把”边“与”点“的关系存下来
就基本完成了图的储存
那我们要记录的只有:
一、针对每一个点:
以当前点为父亲点的的第一条边
二、针对每一条边:
1.边的编号
2.该边所指向的点(儿子节点)
3.下一条以当前边连接的父亲节点为父亲节点的边
4.点权(不少问题中都牵涉到点权,如最短路,最小生成树。。。可根据问题需要选择记录或不记录)
CODE:
//last[i]代表第一条以i为父亲节点的边(记录边的编号)
int last[N*2]; //N*2 怕有双向边
//cnt:边的编号
int cnt;
struct edge//存边
{
//to:这条边指向的儿子节点(记录点的编号)
int to;
//next下一条以当前边连接的父亲节点为父亲节点的边
int next;
//边的权值
int value;
} e[N*2];//下标为边的编号
void insert(int u,int v,int val)// 建边(u为父亲点,v为儿子点)
{
//新加一条边 (编号++)
++cnt;
//儿子节点为v
e[cnt].to=v;
//附上边权
e[cnt].value=val;
//更新当前第一条以u为父亲点的边
e[cnt].next=last[u];
last[u]=cnt;
}
int main()
{
// 如果我想建个图。。。(如下)
// (括号里的数是边权)
// 1
// /
// (1) /
// /
// 2
// /\
// (3) / \ (2)
// / \
// 3------4
// (4)
//如下建的是双向边(双向边建两次边,单向边只要一次)
insert(1,2,1);
insert(2,1,1);
insert(2,3,3);
insert(3,2,3);
insert(2,4,2);
insert(4,2,2);
insert(3,4,4);
insert(4,3,4);
return 0;
}
教程2. 图的遍历:
图的遍历我们一般采用DFS(深度优先)
遍历顺序如下图
很好写,直接上代码
CODE:
//vis[i]代表i被遍历过
bool vis[N];
void dfs(int x) {
//打上标记,该点被遍历过
vis[x]=1;
for(int i=last[x]/*从第一条边开始*/;i;i=e[i].next/*跳到下一条边*/)
{
//如果此节点被遍历过,请跳过
if(vis[e[i].to]) continue;
//遍历儿子节点
dfs(e[i].to);
}
}
实战
题目:树的结点计数
Description
第一行是一个整数(),表示计算机的台数,计算机被编号为。 下面行,每行包括两个整数, ,表示和这两台计算机之间由一条网线连接。 号点为根
Format
Input
第一行给出数字
接下来行描述这个树
Output
给出N行,分别表示从1号到N号点,每个点有多少个子结点
Samples
输入数据 1
3
1 2
1 3
输出数据 1
2
0
0
Limitation
1s, 1024KiB for each test case.
CODE:
#include<bits/stdc++.h>
using namespace std ;
const int N=50000;
int n,m;
int a,b;
int las[N*2];
int cnt;
int son[N];
bool vis[N];
struct edge
{
int to,last;
} e[N*2];
void insert(int u,int v)
{
++cnt;
e[cnt].to=v;
e[cnt].last=las[u];
las[u]=cnt;
}
void dfs(int x) {
son[x]=1;
vis[x]=1;
for(int i=las[x];i;i=e[i].last) {
if(vis[e[i].to]) continue;
dfs(e[i].to);
//加上每个儿子的子节点个数
son[x]+=son[e[i].to];
}
}
int main()
{
cin>>n;
for(int i=1;i<n;i++) {
cin>>a>>b;
insert(a,b);
insert(b,a);
}
dfs(1);
for(int i=1;i<=n;i++) {
//输出时减去自己
cout<<son[i]-1<<endl;
}
return 0;
}