其他分享
首页 > 其他分享> > 链式前向星 牛客训练营C 红和蓝

链式前向星 牛客训练营C 红和蓝

作者:互联网

链式前向星 牛客训练营C 红和蓝

链式前向星

图结构一般有邻接表和邻接矩阵,其中邻接表更适合边比较少的情况。
假设有图有n个顶点,m个边

一般的邻接表的定义为

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>

链式前向星

链式前向星存储的是图中所有的边,其实质上就是一个邻接表的结构,保存的是 以某个顶点为起始顶点的所有边的集合。

参考博文 链式前向星

比较重要的两个概念:

遍历的时候从最后一条边,到上一条边,一直遍历完所有以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:遍历树,操作消去<叶子父亲>节点对

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;
}
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