简要复习了bfs,dfs
DFS(深度优先搜索)是一种递归或者栈结构实现的搜索算法,可以用于无权图或者有权图(边权值相等)。
算法步骤如下:
- 初始化一个标记数组 visited,表示每个节点是否已被访问;
- 从起始节点开始,递归访问它的所有未被访问过的邻居节点。
int visited[100]; // 标记节点是否已访问
void dfs(int u, int n)
{
visited[u] = 1; // 标记节点 u 已被访问
printf("%d ", u); // 访问节点 u
for (int v = 0; v < n; v++)// 遍历节点 u 的所有邻居节点 v
{
if (graph[u][v] && !visited[v]) // 如果存在边 uv,且 v 未被访问过
{
dfs(v, n); // 递归访问节点 v
}
}
}
BFS(广度优先搜索)是一种逐层扫描搜索算法,可以用于无权图或者有权图(边权值相等)。
算法步骤如下:
- 初始化一个标记数组 visited 和一个队列 queue,将起始节点标记为已访问,并将其入队;
- 当队列非空时,取出队首节点,遍历它的所有邻居节点:若邻居节点未被访问,则标记为已访问并入队;
- 重复步骤2,直到队列为空。
int graph[10][10]; // 邻接矩阵存储图
int visited[100]; // 标记节点是否已访问
int duilie[100]; // 队列
void chushihuaGraph(int n)// 初始化邻接矩阵
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
graph[i][j] = 0;
}
}
}
void bfs(int start, int n)
{
int head = 0, tail = 0;
visited[start] = 1; // 起始节点已经访问过,标记为已访问
duilie[tail++] = start; // 将起始节点加入队列
while (head < tail) // 队列非空
{
int u = duilie[head++]; // 取出队首节点
printf("%d ", u); // 访问节点 u
for (int v = 0; v < n; v++) // 遍历所有邻居节点
{
if (graph[u][v] && !visited[v]) // 如果存在边 uv,且 v 未被访问过
{
visited[v] = 1; // 标记节点 v 已被访问
duilie[tail++] = v; // 将节点 v 加入队列
}
}
}
}
难度中等
259
在歌曲列表中,第 i 首歌曲的持续时间为 time[i] 秒。
返回其总持续时间(以秒为单位)可被 60 整除的歌曲对的数量。形式上,我们希望下标数字 i 和 j 满足 i < j 且有 (time[i] + time[j]) % 60 == 0。
示例 1:
输入:time = [30,20,150,100,40]输出:3解释:这三对的总持续时间可被 60 整除:(time[0] = 30, time[2] = 150): 总持续时间 180(time[1] = 20, time[3] = 100): 总持续时间 120(time[1] = 20, time[4] = 40): 总持续时间 60
示例 2:
输入:time = [60,60,60]输出:3解释:所有三对的总持续时间都是 120,可以被 60 整除
看题先想到遍历解决,但是在时间上需求过高,容易时间超限。
int numPairsDivisibleBy60(int* time, int timeSize)
{
int count = 0;
for(int i = 0 ; i < timeSize ; i ++)
for(int j = timeSize - 1 ; j > i ; j --)
{
if((time[i] + time[j]) % 60 == 0)
{
count ++;
}
}
return count ;
}
然后根据官方题解:
组合数学
思路
需要返回其总持续时间(以秒为单位)可被 6060 整除的歌曲对的数量,因此,每首歌曲对结果的影响因素是它的持续时间除以 6060 后的余数。可以用一个长度为 6060 的数组 \textit{cnt}cnt,用来表示余数出现的次数。然后分情况统计歌曲对:
余数为 00 的歌曲。他们需要与余数为 00 的歌曲组成对,但不能与自己组成对。歌曲对的数量为 \textit{cnt}[0] \times (\textit{cnt}[0]-1)/2cnt[0]×(cnt[0]−1)/2。
余数为 3030 的歌曲。他们需要与余数为 3030 的歌曲组成对,但不能与自己组成对。歌曲对的数量为 \textit{cnt}[30] \times (\textit{cnt}[30]-1)/2cnt[30]×(cnt[30]−1)/2。
余数为 i, i\in[1,29]i,i∈[1,29] 的歌曲。他们需要与余数为 60-i60−i 的歌曲组成对。歌曲对的数量为 \sum_{i=1}^{29}\textit{cnt}[i] \times \textit{cnt}[60-i]∑
i=1
29
cnt[i]×cnt[60−i]。
余数为 i, i\in[31,59]i,i∈[31,59] 的歌曲。已经在上一部分组对过,不需要重复计算。
把这几部分求和,就可以得到最后的对数。
额,还要注意数据提供的范围,需要longlong型数据来执行,否侧测试数据会超出范围。
int numPairsDivisibleBy60(int* time, int timeSize)
{
long long count[60]={0},num=0;
memset(count , 0 , sizeof(count));
for (int i = 0; i < timeSize; i++)
{
count[time[i] % 60] ++;
}
for (int i = 1; i < 30; i++)
{
num += count[i] * count[60 - i]; //两两组合相乘即可
}
//理解为Cn2
num +=(long long)(count[0] * (count[0] - 1) / 2);
num +=(long long)(count[30] * (count[30] - 1) / 2);
return num;
}
还有一种题解,感觉和官解差不多,但感觉更好理解
使用哈希表来进行优化。
具体来说,我们可以遍历一遍歌曲列表,对于每首歌曲的持续时间 time[i],计算其除以 60 的余数 num1。然后在哈希表中查找是否存在 60 - num1 的键值对,若存在,则将对应值加入计数器 count 中。
int numPairsDivisibleBy60(int* time, int timeSize){
int count = 0;
int jishu[60] = {0};
for (int i = 0; i < timeSize; i++)
{
int num1 = time[i] % 60;
int num2 = (60 - num1) % 60;
count += jishu[complement];
jishu[remainder]++;
}
return count;
}