链式前向星 牛客训练营C 红和蓝
作者:互联网
链式前向星 牛客训练营C 红和蓝
链式前向星
图结构一般有邻接表和邻接矩阵,其中邻接表更适合边比较少的情况。
假设有图有n个顶点,m个边
- 邻接表保存的数据结构为,每个顶点包含那几条边。则遍历整个图的时间复杂度为 O ( m ) O(m) O(m)
- 邻接矩阵则暴力,二维矩阵, G [ i ] [ j ] G[i][j] G[i][j]代表i到j的边的权重。则遍历整个图的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
一般的邻接表的定义为
struct graph{
vector<int> to;
} G[n];
// 图G有n个顶点, 以该顶点为起始点的边有 G[i].to.size() 个
// 遍历该点所有相邻的顶点为
for (auto & x : G[i].to) {
// TODO <i, x>即为其中一条边
}
// 如果图存在权重,需要自定义 边 Edge 然后 vector<int> 修改为 vector<Edge>
- 以上可以看到
vector<int> to
是一个动态数组,如果不想利用动态数组。则可以借助更方便使用的 链式前向星 的数据结构来存储图。
链式前向星
链式前向星存储的是图中所有的边,其实质上就是一个邻接表的结构,保存的是 以某个顶点为起始顶点的所有边的集合。
比较重要的两个概念:
head[i]
保存的是以i为起始点的边的最后一条边next
保存的是以i为起始点的边的上一条边。
遍历的时候从最后一条边,到上一条边,一直遍历完所有以i为起始点的边。
举个例子,5个顶点7条边的图,第二至第八行是边的起始和结束顶点,权重信息。对所有边编号为0-6
5 7
1 2 1
2 3 2
3 4 3
1 3 4
4 1 5
1 5 6
4 5 7
其中边的结构为
struct star {
int to, next; // to 代表边的另一个顶点,next表示上一个以i为起始点的边的编号
} edge[200020];
只需要保存边的另一个顶点,和next即可,其中next是在edge中的索引。head[x]中x是边的起始顶点。
具体操作如下:
int head[100010]; // 链式前向星 最后一个以x为起始边的边的编号
struct star {
int to, next; // to 代表边的另一个顶点,next表示上一个以x为起始点的边的编号
} edge[200020];
void addedge(int x, int y) {
edge[++tot].to = y; // 第tot条边的另一个顶点是y
edge[tot].next = head[x]; // 用当前的以x为结尾的最后一条边的编号,来更新为上一条边
head[x] = tot; // 当前边的编号,用当前边的编号作为当前以x为起始顶点的最后一条边的编号
}
// 遍历cur所有相邻的边,
for (int i=head[cur]; i!=-1; i=edge[i].next) {
// TODO
// head[cur] 代表以cur起始的边的最后一个边的编号
// edge[i].next 代表以cur起始的边的上一个边的编号
// 一直遍历完所有的以cur起始的所有的边 此时 edge[i].next 上一条边的编号应该为初始化的-1,即不存在上一条边了
}
C 红和蓝
解题思路
- 叶子节点相连的只有父亲节点,所以必然与父亲节点同色,否则叶子节点不满足 有且仅有蓝/红。
- 第一次由下往上遍历树,每次消去一对<叶子,父亲>节点,然后递归消去<叶子,父亲>节点 并标记为相同的符号,如果最后只剩下一个节点,则说明无法按要求染色,否则就可以按照要求染色。
- 第二次由上往下遍历树,递归给节点染色,如果和父节点标记相同染同色,否则染异色。
重点1:构造图结构
利用链式前向星
int head[100010]; // 链式前向星 最后一个以i为起始边的边的编号
struct star {
int to, next; // to 代表边的结尾,next表示上一个以i为起始点的边的编号
} edge[200020];
void addedge(int x, int y) {
edge[++tot].to = y;
edge[tot].next = head[x];
head[x] = tot; // 当前边的编号
}
重点2:遍历树,操作消去<叶子父亲>节点对
- 第一次从上往下遍历,
dfs1
表示每次标记以cur为根节点的树。
输入: 当前节点,其父节点
算法:遍历完所有cur的子树,判断当前cur节点是否被配对,如果未被配对,则只能与父节点配对,如果此时父节点已经配对,则说明涂色不成功。利用cnt来标记配对成功,每次配对成功cnt++,并利用f[]数组来存储当前cnt;
输出:无输出。主要在算法阶段,判断是否满足要求,以及节点同色的配对操作。
void dfs1(int cur, int fa) {
// 标记以cur为根的子树,且fa是cur的父节点
// 遍历所有以cur为起始的边
for (int i = head[cur]; i != -1; i = edge[i].next) {
if (edge[i].to == fa) continue;
dfs1(edge[i].to, cur);
// 此时 以 edge[i].to 为根节点的树已经标记好了
}
// 判断cur是否被配对。如果没有被配对,则和父节点
//printf("f[%d]=%d \t", cur, f[cur]);
if (f[cur] == 0) {
if (f[fa] != 0) {boo=0; return; }
f[cur] = f[fa] = ++cnt;
}
//printf("f[%d]=%d \n", cur, f[cur]);
return;
}
- 第二次由上往下遍历,在配对可以成功的情况下,判断当前节点和父节点是否配对,配对同色,不配对异色,用col[]来存储
输入: 当前节点,其父节点
算法:给以当前节点为根的树进行填色,判断f[cur]和f[fa]是否相同 相同则同色,不同则异色,然后给其子树进行填色即可。
void dfs2(int cur, int fa) {// 标记cur和fa的颜色
for (int i= head[cur]; i!=-1;i=edge[i].next) {
if (edge[i].to==fa) continue;
// 给当前节点进行填色
if (f[ edge[i].to ] == f [cur] ) {
col[ edge[i].to ] = col[cur];
} else {
col[ edge[i].to ] = col[cur]^1;
}
// 给所有子树进行填色
dfs2(edge[i].to, cur);
}
}
实现代码
坑:注意涂色时要判断f[0]是否被涂色,如果未被涂色时才是可以涂色的
#include <bits/stdc++.h>
using namespace std;
int n, tot = 0;
int head[100010]; // 链式前向星 最后一个以i为起始边的边的编号
struct star {
int to, next; // to 代表边的结尾,next表示上一个以i为起始点的边的编号
} edge[200020];
void addedge(int x, int y) {
edge[++tot].to = y;
edge[tot].next = head[x];
head[x] = tot; // 当前边的编号
}
int f[100010]; // 涂色的编号 配对结果
int col[100010]; // 涂色结果
int cnt = 0;
bool boo=1;
void dfs1(int cur, int fa) {
// 标记以cur为根的子树,且fa是cur的父节点
// 遍历所有以cur为起始的边
for (int i = head[cur]; i != -1; i = edge[i].next) {
if (edge[i].to == fa) continue;
dfs1(edge[i].to, cur);
// 此时 以 edge[i].to 为根节点的树已经标记好了
}
//printf("f[%d]=%d \t", cur, f[cur]);
if (f[cur] == 0) {
if (f[fa] != 0) {boo=0; return; }
f[cur] = f[fa] = ++cnt;
}
//printf("f[%d]=%d \n", cur, f[cur]);
return;
}
void dfs2(int cur, int fa) {// 标记cur和fa的颜色
for (int i= head[cur]; i!=-1;i=edge[i].next) {
if (edge[i].to==fa) continue;
if (f[ edge[i].to ] == f [cur] ) {
col[ edge[i].to ] = col[cur];
} else {
col[ edge[i].to ] = col[cur]^1;
}
dfs2(edge[i].to, cur);
}
}
int main() {
scanf("%d", &n);
memset(head, -1, sizeof(head));
memset(edge, -1, sizeof(edge));
for (int i=1; i<n;i++) {
int l,r;
scanf("%d%d", &l,&r);
addedge(l,r);
addedge(r,l);
}
dfs1(1, 0);
// 坑位: f[0]!=0 ? 1和0一起涂色了
if ( f[0]!=0 || !boo ) printf("-1\n");
else {
col[1] = 1;
dfs2(1, 0);
for (int i=1; i<=n; i++) {
if (col[i]) {
printf("R");//printf("%d ", f[i]);
} else {
printf("B");//printf("%d ", f[i]);
}
}
printf("\n");
}
return 0;
}
标签:head,cur,int,训练营,next,牛客,edge,节点,前向星 来源: https://blog.csdn.net/qq_32507417/article/details/113853118