题目描述
难度分:1500
输入n(2≤n≤3×105),m(1≤m≤3×105)和m个数对(a[i],b[i]),元素范围 [1,n]且a[i]≠b[i]。
能否找到两个数x和y,使得每个数对中至少有一个数是x或者是y?
输出YES
或NO
。
输入样例1
4 6
1 2
1 3
1 4
2 3
2 4
3 4
输出样例1
NO
输入样例2
5 4
1 2
2 3
3 4
4 5
输出样例2
YES
输入样例3
300000 5
1 2
1 2
1 2
1 2
1 2
输出样例3
YES
算法
BFS
依题意,如果有解,x必定在a[1]或者b[1]之中,分别尝试一下即可。假设x=a[1],然后从i=1开始进行BFS
,每次检查下一个数对(a[i+1],b[i+1])中有没有x:
- 有就直接跳过,说明截止到目前为止,所有数对都可以被x覆盖掉(数对中只要有x或y就称为被覆盖)。
- 没有就说明a[i+1]和b[i+1]中有一个是y,分别尝试一下。
这时候我们发现状态是不够的,在BFS
的过程中应该记录截止到目前为止经过了几个不同的数,可以让队列存储二元组(数对编号i,截止到数对i经过的数字集合)。因此,只要可以遍历到i=m,并且在这个过程中集合的大小都没有超过过2,说明所有数对都可以被集合中的两个数给覆盖掉,输出YES
即可;如果没有遇到过这种情况,就在BFS
遍历完成后输出NO
。
最后还需要注意一点的就是CodeForces卡哈希!
复杂度分析
时间复杂度
本质上是用BFS
遍历m个数对,时间复杂度是O(m)的。
空间复杂度
队列中存储的二元组可以看成是O(1)的,因为我们可以控制集合的大小不超过2,这样二元组中最多只有三个元素。而BFS
的额外空间复杂度是O(m)的,因此算法整体的额外空间复杂度也是O(m)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
const int N = 300010;
int n, m, a[N], b[N];
bool bfs(queue<pair<int, set<int>>>& q) {
while(!q.empty()) {
auto cur = q.front();
q.pop();
int i = cur.first;
auto& st = cur.second;
int sz = st.size();
if(i == m) return true;
i++;
if(st.count(a[i]) || st.count(b[i])) {
q.push({i, st});
}else {
if(sz == 1) {
set<int> st1 = {st.begin(), st.end()}, st2 = {st.begin(), st.end()};
st1.insert(a[i]);
st2.insert(b[i]);
q.push({i, st1});
q.push({i, st2});
}
}
}
return false;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) {
scanf("%d%d", &a[i], &b[i]);
}
queue<pair<int, set<int>>> q;
q.push({1, {a[1]}});
if(bfs(q)) {
puts("YES");
}else {
while(!q.empty()) q.pop();
q.push({1, {b[1]}});
if(bfs(q)) puts("YES");
else puts("NO");
}
return 0;
}