PTA 卷图 (25分)(二分图+bfs+并查集)

7-10 卷图 (25分)

作为优秀大学的优秀学生,小w必然活在一个极度内卷的环境里:,每一次的大报告,每一次的期末考试,每一次的作业…

而在大x的某堂课上,在老师分完分组任务后,小w惊奇地发现班上内卷的同学自动分好组了。精通离散数学的小w将内卷的同学抽象成一个个的点,然后必不可能分到一个组的同学之间连出边,小w发现了这张图有如下性质:

图有n个结点,并且图上的n个节点可以分成A,B两个集合;

  1. A∩B=∅

  2. A集合内的点均无连边

  3. B集合内的点均无连边

小w称这样的图为卷图,问题来了,小w自己突然有了很多奇奇怪怪的图,请你判断一下哪些是卷图,哪些不是卷图。

输入格式:

注意本题有多组数据。

每组测试数据第一行将读入一个数t(1≤t≤20),代表测试数据的组数。

之后的t组数据,每组数据的第一行读入两个数n,m(1≤n≤10^​5​​ ,1≤m≤min(​2​n⋅(n−1)​​ ,200000)),代表此图由n个点和m条边组成。

接下来m行,每行两个整数u,v(1≤u,v≤n),u≠v,代表点u,v之间连有一条边。

数据保证给出的图是简单图,即无重边与自环

输出格式:

对于每组样例的每张图,如果是卷图,输出Ye5!juantu

否则,输出N0!

输入样例:

在这里给出一组输入。例如:

2
3 3
1 2
2 3
3 1
3 2
1 2
1 3

输出样例:

在这里给出相应的输出。例如:

N0!
Ye5!juantu

提示

对于样例1,第一幅图为:
在这里插入图片描述
无法找到这样的两个点集A,B,故不是卷图

第二张图:
在这里插入图片描述
红色的点和蓝色的点构成题意所示的点集,故可以构成卷图。

解题

本题采用图的链式存储结构,输入的图不一定是一个连通图,因此要用并查集分别对每个集合的图进行bfs搜索,当图中存在奇数环时,表示不是二分图,bfs搜索时,对每个结点标记其bfs树层数,当遍历到没访问的结点时,访问并标记,当是已访问的结点时,说明有环了,而如果当前节点与已访问的结点的层数是相同的,说明是一个奇数环,就不是二分图。
通过画顶点较少的简单图的结构,观察,总结规律:
在这里插入图片描述

代码

#include <algorithm>  //7-10 卷图 (25分)
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 1e5 + 2;
bool vis[maxn];
int Set[maxn];

//找出所有的环, 如果存在奇数顶点构成的环, 则不是卷图 (自己列举几个简单图)

/*复习一下指针的链式存储, C语言数据结构*/
struct AdjVNode {
    
      //邻接链表的链
    AdjVNode *Next;
    int adjV;
};
typedef struct VNode {
    
    
    AdjVNode *FirstEdge;  //无权图
    int k;                //记录bfs树的层数
} AdjVList[maxn];
typedef struct GNode {
    
    
    int Nv, Ne;
    AdjVList L;
} * LGraph;
typedef struct ENode {
    
    
    int v1, v2;
} * Edge;

void InsertEdge(LGraph G, Edge E) {
    
      //给图插入边
    AdjVNode *A;
    A = new AdjVNode, A->adjV = E->v2;
    A->Next = G->L[E->v1].FirstEdge, G->L[E->v1].FirstEdge = A;

    A = new AdjVNode, A->adjV = E->v1;
    A->Next = G->L[E->v2].FirstEdge, G->L[E->v2].FirstEdge = A;
}

LGraph CreateGraph(int Nv, int Ne) {
    
      //新建图
    LGraph G = new GNode();
    G->Nv = Nv, G->Ne = Ne;
    return G;
}

void DestroyGraph(LGraph G) {
    
      //删除图, 释放内存
    AdjVNode *A;
    for (int i = 1; i <= G->Nv; i++)
        while (G->L[i].FirstEdge) {
    
    
            A = G->L[i].FirstEdge;
            G->L[i].FirstEdge = A->Next;
            delete A;
        }
    delete G;
    G = NULL;
}

bool bfs(LGraph G, int start) {
    
    
    queue<int> q;
    q.push(start);
    G->L[start].k = 1;
    vis[start] = true;
    AdjVNode *A;
    while (!q.empty()) {
    
    
        int v = q.front();  //当前节点
        int k = G->L[v].k;
        q.pop();
        A = G->L[v].FirstEdge;
        while (A) {
    
    
            if (!vis[A->adjV]) {
    
      //如果访问到新节点, 入队
                vis[A->adjV] = true;
                q.push(A->adjV);
                G->L[A->adjV].k = k + 1;
            } else if (k == G->L[A->adjV].k) {
    
    
                //如果是已访问的结点, 说明找到环路了,而如果是同一层说明是奇数环
                return false;
            }
            A = A->Next;
        }
    }
    return true;
}
/*并查集*/
int FindSet(int x) {
    
    
    if (Set[x] < 0) return x;
    return Set[x] = FindSet(Set[x]);
}
void Union(int r1, int r2) {
    
    
    if (r1 == r2) return;
    if (Set[r1] < Set[r2])
        Set[r1] += Set[r2], Set[r2] = r1;
    else
        Set[r2] += Set[r1], Set[r1] = r2;
}

int main() {
    
    
    int T, n, m;
    cin >> T;
    LGraph G = NULL;
    vector<int> vv;
    while (T--) {
    
    
        memset(vis, false, sizeof(vis));
        memset(Set, -1, sizeof(Set));
        vv.clear();

        scanf("%d %d", &n, &m);
        G = CreateGraph(n, m);
        Edge E = new ENode();
        for (int i = 0; i < m; i++) {
    
    
            scanf("%d %d", &E->v1, &E->v2);
            Union(FindSet(E->v1), FindSet(E->v2));
            InsertEdge(G, E);
        }

        /*求解判断*/
        for (int i = 1; i <= n; i++)  //将所有集合的根加入数组记录
            if (Set[i] < 0) vv.push_back(i);
        bool bo = true;
        for (int i = 0; i < vv.size(); i++) {
    
    
            int start = vv[i];
            bo = bfs(G, start);
            if (!bo) {
    
    
                printf("N0!\n");
                break;
            } else if (i == vv.size() - 1)  //如果bo = true, 且是最后一个
                printf("Ye5!juantu\n");
        }

        delete E;
        DestroyGraph(G);
    }

    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45349225/article/details/109357260