文章目录
1.LeetCode:1405. 最长快乐字符串
如果字符串中不含有任何 ‘aaa’,‘bbb’ 或 ‘ccc’ 这样的字符串作为子串,那么该字符串就是一个「快乐字符串」。
给你三个整数 a,b ,c,请你返回 任意一个 满足下列全部条件的字符串 s:
s 是一个尽可能长的快乐字符串。
s 中 最多 有a 个字母 ‘a’、b 个字母 ‘b’、c 个字母 ‘c’ 。
s 中只含有 ‘a’、‘b’ 、‘c’ 三种字母。
如果不存在这样的字符串 s ,请返回一个空字符串 “”。
示例 1:
输入:a = 1, b = 1, c = 7
输出:“ccaccbcc”
示例 2:
输入:a = 2, b = 2, c = 1
输出:“aabbc”
示例 3:
输入:a = 7, b = 1, c = 0
输出:“aabaa”
提示:
0 <= a, b, c <= 100
a + b + c > 0
这道题就很明显的是贪心了,我们在构造字符串的时候首先选取a,b,c中剩余数量最大的,然后检查如果将该字符插入到字符串中,是否会出现aaa这样的字符串,如果出现的话就选择插入剩余数量第二大的字符。就这样一直到字符用光。
要达到这种效果我们就需要利用优先级队列了,其中有三个元素,每一个都是pair,first为字符数量,second为字符种类,由于字符在存储的时候都是按照ascii码来的,这里的second就写成字符与‘a’差的ascii码。
代码如下
class Solution {
public:
string longestDiverseString(int a, int b, int c) {
priority_queue<pair<int,int>> q;
if(a) q.push(make_pair(a,0));
if(b) q.push(make_pair(b,1));
if(c) q.push(make_pair(c,2));
string ans="";
while(!q.empty()){
auto tmp=q.top();
q.pop();
if(ans.size()>=2&&tmp.second+'a'==ans[ans.size()-1]&&tmp.second+'a'==ans[ans.size()-2]){
if(q.empty()){
return ans;
}
auto temp=q.top();
q.pop();
ans+='a'+temp.second;
--temp.first;
if(temp.first){
q.push(temp);
}
q.push(tmp);
}else {
ans+=tmp.second+'a';
--tmp.first;
if(tmp.first){
q.push(tmp);
}
}
}
return ans;
}
};
2.LeetCode:2285. 道路的最大总重要性
给你一个整数 n ,表示一个国家里的城市数目。城市编号为 0 到 n - 1 。
给你一个二维整数数组 roads ,其中 roads[i] = [ai, bi] 表示城市 ai 和 bi 之间有一条 双向 道路。
你需要给每个城市安排一个从 1 到 n 之间的整数值,且每个值只能被使用 一次 。道路的 重要性 定义为这条道路连接的两座城市数值 之和 。
请你返回在最优安排下,所有道路重要性 之和 最大 为多少。
示例 1:
输入:n = 5, roads = [[0,1],[1,2],[2,3],[0,2],[1,3],[2,4]]
输出:43
示例 2:
输入:n = 5, roads = [[0,3],[2,4],[1,3]]
输出:20
提示:
2 <= n <= 5 * 1e4
1 <= roads.length <= 5 * 1e4
roads[i].length == 2
0 <= ai, bi <= n - 1
ai != bi
没有重复道路。
这道题看上去像是图的搜索问题,但仔细观察的话会发现这里边的值是u+v,并且计算结果的时候会把所有的边都算一次。那么这样就没必要那么麻烦了,我们直接统计各个顶点的度,然后按照度的大小来进行赋值,再把他们累加到一起即可。
class Solution {
public:
long long maximumImportance(int n, vector<vector<int>>& roads) {
vector<int> degs(n,0);
for(auto i:roads){
++degs[i[0]];
++degs[i[1]];
}
sort(degs.begin(),degs.end());
long long ans=0;
for(int i=0;i<n;++i){
ans+=1LL*degs[i]*(i+1);
//价值是从1开始的
//*1LL是为了将结果转换成longlong避免溢出
}
return ans;
}
};
3.LeetCode:675. 为高尔夫比赛砍树
你被请来给一个要举办高尔夫比赛的树林砍树。树林由一个 m x n 的矩阵表示, 在这个矩阵中:
0 表示障碍,无法触碰
1 表示地面,可以行走
比 1 大的数 表示有树的单元格,可以行走,数值表示树的高度
每一步,你都可以向上、下、左、右四个方向之一移动一个单位,如果你站的地方有一棵树,那么你可以决定是否要砍倒它。
你需要按照树的高度从低向高砍掉所有的树,每砍过一颗树,该单元格的值变为 1(即变为地面)。
你将从 (0, 0) 点开始工作,返回你砍完所有树需要走的最小步数。 如果你无法砍完所有的树,返回 -1 。
可以保证的是,没有两棵树的高度是相同的,并且你至少需要砍倒一棵树。
示例 1:
输入:forest = [[1,2,3],[0,0,4],[7,6,5]]
输出:6
示例 2:
输入:forest = [[1,2,3],[0,0,0],[7,6,5]]
输出:-1
示例 3:
输入:forest = [[2,3,4],[0,0,5],[8,7,6]]
输出:6
提示:
m == forest.length
n == forest[i].length
1 <= m, n <= 50
0 <= forest[i][j] <= 1e9
这道题就是很基础的bfs了,按照题目的要求我们必须按照高度从小到大的顺序来砍树,那我们的路径就只能是每次找一条最短到达当前最小高度的树的路然后把他们累加。
这里我们利用一个小根堆来对高度进行操作,然后每次取出堆顶作为我们的目的地,起始位置为strat=[0,0],然后走一遍bfs得到最短距离累加。如果最短距离返回的是-1就说明我们无法到达目的地,直接返回-1.最终队列为空的时候我们返回累计的距离。
具体到bfs怎么处理就很简单了,很基础的图的搜索问题。先定义一个数组用来记录步数,开始都设置成-1代表没有走到,起点的步数设置为0。首先将起点加入队列。每次移动先取出堆顶作为当前点,如果当前点为目标点直接返回累计的步数,否则往四个方向搜索,如果越界或者之前搜索过该点(这里同时也保证了路径最短,可能需要自己思考,所以在这里提示一下)或者该点是障碍这个方向不继续进行,更新的步数就为上一步的步数+1,并把该点入队。
class Solution {
class Node{
public:
int x,y,h;
Node(){}
Node(int _x,int _y,int _h): x(_x),y(_y),h(_h){}
bool operator< (const Node& b) const{
return h>b.h;
}
};
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
int row,col;
int bfs(vector<vector<int>>& forest,Node& start,Node& target){
int step[51][51];
memset(step,-1,sizeof(step));
queue<Node> q;
q.push(start);
step[start.x][start.y]=0;
while(!q.empty()){
Node cur=q.front();
q.pop();
if(cur.x==target.x&&cur.y==target.y){
return step[cur.x][cur.y];
}
Node next;
for(auto i:dir){
next.x=i[0]+cur.x;
next.y=i[1]+cur.y;
if(next.x==-1||next.y==-1||next.x==row||next.y==col){
continue;
}
if(!forest[next.x][next.y]){
continue;
}
if(step[next.x][next.y]!=-1){
continue;
}
step[next.x][next.y]=step[cur.x][cur.y]+1;
q.push(next);
}
}
return -1;
}
public:
int cutOffTree(vector<vector<int>>& forest) {
row=forest.size();
col=forest[0].size();
priority_queue<Node> q;
for(int i=0;i<row;++i){
for(int j=0;j<col;++j){
if(forest[i][j]>1)
q.push(Node(i,j,forest[i][j]));
}
}
Node start=Node(0,0,forest[0][0]);
int ans=0;
while(!q.empty()){
Node target=q.top();
q.pop();
int dist=bfs(forest,start,target);
if(dist==-1){
return -1;
}
ans+=dist;
start=target;
}
return ans;
}
};
4.LeetCode:659. 分割数组为连续子序列
给你一个按升序排序的整数数组 num(可能包含重复数字),请你将它们分割成一个或多个长度至少为 3 的子序列,其中每个子序列都由连续整数组成。
如果可以完成上述分割,则返回 true ;否则,返回 false 。
示例 1:
输入: [1,2,3,3,4,5]
输出: True
示例 2:
输入: [1,2,3,3,4,4,5,5]
输出: True
示例 3:
输入: [1,2,3,4,4,5]
输出: False
提示:
1 <= nums.length <= 10000
这道题是很有意思的一道题目,他的方法很巧妙不容易想出来。
这里我们需要准备一个表,key为我们构造的字符串的结尾,value为一个优先级队列存储以key结尾所有字符串的长度。构造字符串的时候我们先去表中寻找比当前数字小1的数字在表中是否出现过,如果出现过就将其删除,当前数字的表中加入其长度 +1,如果没有出现或者上一个数字的堆为空,就代表没有以上一个数字为结尾的字符串,我们直接在现在数字的表中加入1。
当数组遍历完毕之后,数组中按照连续整数排列的子串都在表中存了下来。只不过我们存放的是他们的结尾和长度(由结尾和长度也可以推算出这个子串),最后我们遍历这个表,如果该元素的value有元素并且堆顶小于3就说明以他的key结尾的所有字串长度不可能大于3,也就是不能把数组拆分为长度至少为三的连续子数组。直接返回false,当图遍历完毕我们返回true.
class Solution {
public:
bool isPossible(vector<int>& nums) {
unordered_map<int,priority_queue<int,vector<int>,greater<int>>> map;
for(int i=0;i<nums.size();++i){
auto it=map.find(nums[i]-1);
if(it==map.end()||!(it->second.size())){
map[nums[i]].push(1);
}else {
int x=it->second.top()+1;
it->second.pop();
map[nums[i]].push(x);
}
}
for(auto it=map.begin();it!=map.end();++it){
if(it->second.size()&&it->second.top()<3){
return false;
}
}
return true;
}
};