C++日常刷题积累
今日刷题汇总 - day027
1、kotori和气球
1.1、题目
1.2、思路
读完题,知道将不同种类的气球进行排列组合,要求相同种类的气球不相邻,求摆成一排m个一共有多少种不同的方案?根据题目和示例分析,属于排列和组合类问题,这里发现直接可以套公式即可,即:n*(n-1)。那么接下来,就是程序实现。
1.3、程序实现 – 排列组合
套公式即可:
#include <iostream>
using namespace std;
const int Mod = 109;
int main()
{
int n,m;
cin >> n >> m;
int ret = n;
for(int i = 0;i < m - 1;i++)
{
ret = ret * (n-1)%Mod;
}
cout << ret << endl;
return 0;
}
2、走迷宫
2.1、题目
2.2、思路
读完题,知道对于一个n*m的网格,按照一定的规则(上、下、左、右),求由一个点(起始点坐标)到另外一个点(终点坐标)的最少移动次数。读完题就知道跟之前做过的搜索类题bfs腐烂的苹果和dfs岛屿数量类似,均采用搜索即可,那么根据题目和示例分析矩阵的是从1开始的计数,那么这道题采用bfs老套路框架,需要方向数组,dx,dy,除此之外还需要利用dist[N][N]标记已经搜索,且不能回溯,同时设置为Int型顺便统计到达第(i,j)位置时的最少距离,最后初始化dist[N][N]为-1,表示未被搜索即可,另外,对立障碍物的处理,还需要像之前做过的题一样利用数据结构/容器存储起来,方便执行判断和控制逻辑,这里使用queue队列结合pair处理坐标键值对即可,接下来,就是程序实现。
2.3、程序实现 – bfs
首先,按照题目要求写好输入,这里最好定义为全局的方便dfs也使用,不用传参,接着写号bfs的框架,定义方向数组dx,dy以及需要的标记和统计距离的dist数组.
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int N = 1010;
char arr[N][N];
int dist[N][N];
int n,m;
int x1,x2,y1,y2;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int bfs()
{
}
int main()
{
cin >> n >> m;
cin >> x1 >> y1 >> x2 >> y2;
//注意题目中从1开始
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
cin >> arr[i][j];
}
}
cout << bfs() << endl;
return 0;
}
接着,完善bfs,首先如果终点本身就是障碍物就直接返回即可,不是则初始化dist数组为-1,标为未被标记,再接着利用队列让已经搜索过的有效点(组成路径的点)进队列,那么首先肯定就是让第一个起始点进队列,并初始化第一个点的距离为0,然后执行控制逻辑,搜索起始点周围四个方向是否满足无障碍,且未被标记的条件,然后继续进队列,标记被搜索同时更新距离,如果搜索到终点则返回距离即可,如果搜索完都没有找到搜索点,则返回-1即可。
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int N = 1010;
char arr[N][N];
int dist[N][N];
int n,m;
int x1,x2,y1,y2;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int bfs()
{
if(arr[x2][y2] == '*')
return -1;
//初始化dist
memset(dist,-1,sizeof(dist));
//建立队列,坐标键值对
queue<pair<int,int>> q;
//入起始点
q.push({x1,y1});
dist[x1][y1] = 0;
//执行逻辑
while(q.size())
{
auto[a,b] = q.front();
q.pop();
//搜索周围有效点位
for(int i = 0;i < 4;i++)
{
int x = a + dx[i];
int y = b + dy[i];
//判断有效范围和未标记点位
if(x >= 1 && x <= n && y >= 0 && y <= m && arr[x][y] == '.' && dist[x][y] == -1)
{
q.push({x,y});
dist[x][y] = dist[a][b] + 1;//更新距离
//判断终点,返回距离即可
if(x == x2 && y == y2)
return dist[x2][y2];
}
}
}
return -1;
}
int main()
{
cin >> n >> m;
cin >> x1 >> y1 >> x2 >> y2;
//注意题目中从1开始
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
cin >> arr[i][j];
}
}
cout << bfs() << endl;
return 0;
}
3、主持人调度(二)
3.1、题目
3.2、思路
读完题,知道基于之前主持人调度(一)的题,改变就是不是让一个主持人完成所有活动,而是完成所有n个活动需要多少个主持人。其中,依然要求一个主持人只能在同一时间只能参与一个活动。那么对于这区间类问题,通常采用端点对齐的方式解决,为了方便理解画个图:
所以综上所述,总结区间类问题解决方案:
(1)、采用左端点或者右端点对齐排序;
(2)、按照顺序依次处理没有分配的区间,然后判断已处理分配好的区间右端点与即将分配的区间左端点是否满足放在哪一个已处理区间的后面;
(3)、选择一个合适的数据结构容器存放优化,避免重复便利比较,这里由于是左端点对齐那么采用小根堆即可满足需求。
那么接下来,就是程序实现。
3.3、程序实现 – 区间端点排序
根据思路分析的思路利用小根堆执行控制逻辑如下:
左端点排序,然后搞个堆(priority_queue优先级队列):
a. 先把第⼀个区间的右端点加⼊到堆中;
b. 遍历后⾯的区间,分情况讨论:
i. 如果左端点⼤于等于堆顶元素,能接在后⾯,⼲掉堆顶,然后把这个区间的右端点加⼊堆;
ii. 否则的话,只能再来⼀个⼈,只把这个区间的右端点加⼊堆。
c. 最后堆的⼤⼩就是需要的⼈数。
priority_queue底层通常使用堆(Heap)这种数据结构来实现,特别是二叉堆。在C++的STL(Standard Template Library)中,priority_queue被实现为一个容器适配器,它默认使用最大堆来存储元素,使得队列头部的元素总是具有最高优先级。但是,通过提供自定义的比较函数,也可以实现最小堆。这里使用仿函数greater处理转小根堆即可。
class Solution {
public:
int minmumNumberOfHost(int n, vector<vector<int> >& startEnd)
{
//sort
sort(startEnd.begin(),startEnd.end());
//小根堆
priority_queue<int,vector<int>, greater<int>> heap;
//存入第一个区间右端点
heap.push(startEnd[0][1]);
//处理剩余区间
for(int i = 1;i < n;i++)
{
//左右端点
int left = startEnd[i][0];
int right = startEnd[i][1];
//判断是否重叠
if(left >= heap.top()) //没有重叠,则入堆,删除前面区间的端点
{
heap.pop();
heap.push(right);
}
else //增加主持人
{
heap.push(right);
}
}
return heap.size();//所以人数就是堆中剩余元素个数
}
};