题目超长,可以进链接看原题。
题目概要
题目描述:
在二维空间上有若干个点,有两队(蓝方和绿方),每队都可以占边。
而当有其中一队占的边有可能有三条首尾相连就输出"yes",否则输出"no"。
输入描述:
第一行一个正整数 T,表示数据组数。
每组数据的第一行有两个正整数 n 和 m,表示场上总据点数为 n,此时蓝方已经形成了 m 条控制链。
接下来 m 行,每行有两个正整数 x 和 y,表示这条控制链由据点 x 和据点 y 形成。
每组数据的第一行有两个正整数 n 和 m,表示场上总据点数为 n,此时蓝方已经形成了 m 条控制链。
接下来 m 行,每行有两个正整数 x 和 y,表示这条控制链由据点 x 和据点 y 形成。
(1 ≤ T ≤ 10,1 ≤ n ≤ 5 × 104,1 ≤ m ≤ 105)
输出描述:
对于每组数据,如果目前场上有可能存在被玩家控制的区域,则输出一行 "yes",否则输出一行 "no"。(均不包含引号)
对于每组数据,如果目前场上有可能存在被玩家控制的区域,则输出一行 "yes",否则输出一行 "no"。(均不包含引号)
解析
这道题其实可以暴力(但不是全暴),看了大佬的方法懂了。
那我们就来梳理一下思路:
- 首先用一个二维数组(edge数组)保存两个点之间的关系(edge[x][y]为0表示中间没边,为1表示中间有边)。
- 三条边首尾相连,我们可以简单的枚举出三个点,看是否首尾相连。如果首尾相连直接出来进行下一组就好了。
- 但是我们知道,现在n非常的大。所以我们不可能全部遍历。这个时候我们用拉姆塞理论(原理看专栏)可以得出n>=6时,一定有一方可以成为三角形。
- 还有大佬用硬核做法,就是随便查找三条边,查到就输出yes就行了。因为yes的可能性很大,随便找节点遍历50w次(随机取样做法,超级佩服想到的大佬)
- 所以我们只要遍历比6小的可能性就好了。详情看代码~
拉姆塞理论
其实这个原理我也不会证明,我们先来说一下定义:
- 用引例描述就是:两个人可以彼此认识,也可以彼此不认识,人数大于或等于6时,则必定有3个人,他们一定彼此认识或彼此都不认识。
你可能会很懵,为什么呢?没事!我会告诉你,我也不知道为什么。orz
所以我们就用一个图例简单理解一下。
- 我们可以看到,前三个图形都可以有方法无法产生三角形区域。
- 但是第四个六边形,一定会产生:我们现在红边已经构成了两个三角形,蓝边没有构成三角形,但是如果把任意红边换成蓝边,都一定会产生蓝色三角形。
- 所以结果就是:我们想拆红三角形,就会产生蓝色三角形。拆蓝色三角形,就会产生红色三角形。
- 差不多就是这样,理解一下~~~
AC代码
#include <iostream> #include <cstring> using namespace std; //代码预处理区 const int MAX = 7; int edge[MAX][MAX];//点与点之间的关系(1为有边) //全局变量区 template<class T>inline void read(T& res);//整型快读函数 //函数预定义区 int main() { int T; read(T); while (T--) { int n, m; read(n); read(m); if (n >= 6) { for (int i = 0; i < m; i++) { int x, y; read(x); read(y); } printf("yes\n"); continue; } //根据拉姆塞理论,n>=6一定有一方三条边可以构成三角形 memset(edge, 0, sizeof(edge)); for (int i = 1; i <= m; i++) { int x, y; read(x); read(y); edge[x][y] = edge[y][x] = 1; } //初始化edge数组 int flag = 0; for (int i = 1; i <= n && !flag; i++) for (int j = i + 1; j <= n && !flag; j++) for (int k = j + 1; k <= n && !flag; k++) if (edge[i][j] == edge[j][k] && edge[j][k] == edge[k][i]) flag = 1; //遍历查找是否有合适的点对 if (flag) printf("yes\n"); else printf("no\n"); } return 0; } template<class T>inline void read(T& res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } //函数区