字节豆油瓶笔试题
题目
题目描述:
抖音上每天都有几亿用户,如果用户 A 和 B 互动不少于 3 次,我们就认为 A 和 B 属于“豆油”,如果 A 和 B 是“豆油”,B 和 C 也是“豆油”, 那么 A 和 C 也互为“豆油”,我们定义“豆油瓶”就是由直系和间接朋友所组成的群体。
给定个 N×N 的矩阵 M,代表抖音上所有用户的互动次数,如果M[i][j]= 5,那么第i个和第j个用户就互动过5次,为 0 的话代表没有互动,对于i = j,即同个用户, 互动次数我们计为0。请你计算并输出发现的抖音上所有“豆油瓶”的个数。
输入描述:
输入第1行:二维数组的行数(列数一样)N
接下来的N行每行N个数字,空格分割
输出描述:
输出发现的“豆油瓶”数量 k
示例1:
输入
3
0 4 0
4 0 0
0 0 0
输出
2
解释:第1个和第2个用户互动超过3次,互为豆油第3个学生和其他人没有互动,自成个豆油瓶
示例2:
输入
3
0 4 0
4 0 6
0 6 0
输出
1
解释:三个用户都在同一个豆油瓶里
题解
方法1:图法
比较容易想到使用BFS/DFS遍历每个有关联的用户,这里给出了一个小规模数据适用的算法
#include <bits/stdc++.h>
using namespace std::chrono;
int n;
int count = 0;
inline bool match(int ** G,const int& a,const int& b)
{
return G[a][b]>=3;
}
void BFS(int **G,int a,int b)
{
int start = a;
std::queue<int> Q;
Q.push(start);
if(match(G,a,b))
count++;
G[a][b] = 0; //set visit
while(!Q.empty()) //取能到达的节点
{
auto _node = Q.front();//取队头节点
Q.pop();
//查找该节点能到达的节点
for(int i=0;i<n;i++)
{
if(match(G,_node,i))
{
Q.push(i);
G[_node][i] = 0; //set neighbour visit
}
}
}
}
void DFS_CLS(int **G,const int a,const int b)
{
if(!match(G,a,b))
return;
G[a][b] = 0; //清除自身
for(int j=0;j<n;j++) //清除邻居
{
if(match(G,a,j)) //对于a的邻居{S},如果s符合条件,则处理s和其邻居
{
DFS_CLS(G,j,a);
}
}
}
void DFS(int **G,const int& a,const int& b)
{
if(!match(G,a,b)) //未找到
return;
//处理目标
count++;
DFS_CLS(G,a,b);
}
int main()
{
std::cin >> n;
int **matrix = new int* [n];
for(int i=0;i<n;i++) //处于数据量考虑,这里不直接在栈空间申请内存。但是使用邻接矩阵表示图在大约n>sqrt(32768*1024/4)时会超过内存限制
{
matrix[i] = new int[n];
for(int j=0;j<n;j++)
{
std::cin >> matrix[i][j];
if(i == j)
matrix[i][j] = 3; //自己和自己至少是一个豆油瓶
}
}
for(int i=0;i<n;i++)
BFS(matrix,i,i);
for(int i=0;i<n;i++)
{
delete[] matrix[i];
}
delete[] matrix;
std::cout<<count;
return 0;
}
方法2:集合法
上一种方法的空间复杂度是O(N^2),非常低效,哪怕改用邻接表也可能不够内存
使用集合法能将空间复杂度降到O(N),由于哈希集合插入和查找的时间为O(1),所以时间复杂度也是O(N)
#include <iostream>
#include <cstring>
#include <unordered_set>
using namespace std;
int n;
int count = 0;
int main()
{
cin >> n;
int tmp;
unordered_set<int> group; //unordered_set底层实现是哈希表,相当于java的Hash_Set
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin >> tmp;
if(tmp>=3) //某用户与其他用户互动>=3
{
//集合中没有人和这两用户有互动,说明是新的豆油瓶
if(group.find(i) == group.end() && group.find(j) == group.end())
{
count++;
}
//set自带去重
group.insert(i);
group.insert(j);
}
}
}
count += (n-group.size()); //加上没有和任何人互动过的孤立豆油
cout<<count;
return 0;
}
这种方法感觉比较取巧,并不是很容易想到