LeetCode 第208场周赛 题解

今天要补课 差点忘了

a.文件夹操作日志搜集器

a.题目

每当用户执行变更文件夹操作时,LeetCode 文件系统都会保存一条日志记录。

下面给出对变更操作的说明:

  • “…/” :移动到当前文件夹的父文件夹。如果已经在主文件夹下,则 继续停留在当前文件夹 。
  • “./” :继续停留在当前文件夹。
  • “x/” :移动到名为 x 的子文件夹中。题目数据 保证总是存在文件夹 x 。

给你一个字符串列表 logs ,其中 logs[i] 是用户在 ith 步执行的操作。

文件系统启动时位于主文件夹,然后执行 logs 中的操作。

执行完所有变更文件夹操作后,请你找出 返回主文件夹所需的最小步数 。

示例 1

输入:logs = [“d1/”,“d2/”,"…/",“d21/”,"./"]
输出:2
解释:执行 “…/” 操作变更文件夹 2 次,即可回到主文件夹

示例 2

输入:logs = [“d1/”,“d2/”,"./",“d3/”,"…/",“d31/”]
输出:3

示例 3

输入:logs = [“d1/”,"…/","…/","…/"]
输出:0

提示

  • 1 <= logs.length <= 103
  • 2 <= logs[i].length <= 10
  • logs[i] 包含小写英文字母,数字,’.’ 和 ‘/’
  • logs[i] 符合语句中描述的格式
  • 文件夹名称由小写英文字母和数字组成

a.分析

这题目已经是最简单的了
也就是说只有

  • 当前文件夹
  • 上一个
  • 进入某个文件夹

不存在字符串为类似 “…/filename/a” 这样的
且某个文件夹到根路径只有一条 那么完全可以忽略是哪个文件 只需要记录是第几层即可

总的时间复杂度为O(n)

a.参考代码

class Solution {
public:
    int minOperations(vector<string>& logs) {
        int ans=0;
        for(auto log:logs){
            if(log=="./")continue;	//当前
            else if(log=="../"){
                if(ans)ans--;	//不是根目录的退后
                else continue;
            }
            else ans++;	//进入了一层文件夹
        }
        return ans;
    }
};

b.经营摩天轮的最大利润

b.题目

你正在经营一座摩天轮,该摩天轮共有 4 个座舱 ,每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱,但每次轮转都需要支付一定的运行成本 runningCost 。摩天轮每次轮转都恰好转动 1 / 4 周。

给你一个长度为 n 的数组 customers , customers[i] 是在第 i 次轮转(下标从 0 开始)之前到达的新游客的数量。这也意味着你必须在新游客到来前轮转 i 次。每位游客在登上离地面最近的座舱前都会支付登舱成本 boardingCost ,一旦该座舱再次抵达地面,他们就会离开座舱结束游玩。

你可以随时停下摩天轮,即便是 在服务所有游客之前 。如果你决定停止运营摩天轮,为了保证所有游客安全着陆,将免费进行所有后续轮转 。注意,如果有超过 4 位游客在等摩天轮,那么只有 4 位游客可以登上摩天轮,其余的需要等待 下一次轮转

返回最大化利润所需执行的 最小轮转次数 。 如果不存在利润为正的方案,则返回 -1 。

示例 1

输入:customers = [8,3], boardingCost = 5, runningCost = 6
输出:3
解释:座舱上标注的数字是该座舱的当前游客数。

  1. 8 位游客抵达,4 位登舱,4 位等待下一舱,摩天轮轮转。当前利润为 4 * $5 - 1 * $6 = $14 。
  2. 3 位游客抵达,4 位在等待的游客登舱,其他 3 位等待,摩天轮轮转。当前利润为 8 * $5 - 2 * $6 = $28 。
  3. 最后 3 位游客登舱,摩天轮轮转。当前利润为 11 * $5 - 3 * $6 = $37 。
    轮转 3 次得到最大利润,最大利润为 $37 。

示例 2

输入:customers = [10,9,6], boardingCost = 6, runningCost = 4
输出:7
解释:

  1. 10 位游客抵达,4 位登舱,6 位等待下一舱,摩天轮轮转。当前利润为 4 * $6 - 1 * $4 = $20 。
  2. 9 位游客抵达,4 位登舱,11 位等待(2 位是先前就在等待的,9 位新加入等待的),摩天轮轮转。当前利润为 8 * $6 - 2 * $4 = $40 。
  3. 最后 6 位游客抵达,4 位登舱,13 位等待,摩天轮轮转。当前利润为 12 * $6 - 3 * $4 = $60 。
  4. 4 位登舱,9 位等待,摩天轮轮转。当前利润为 * $6 - 4 * $4 = $80 。
  5. 4 位登舱,5 位等待,摩天轮轮转。当前利润为 20 * $6 - 5 * $4 = $100 。
  6. 4 位登舱,1 位等待,摩天轮轮转。当前利润为 24 * $6 - 6 * $4 = $120 。
  7. 1 位登舱,摩天轮轮转。当前利润为 25 * $6 - 7 * $4 = $122 。
    轮转 7 次得到最大利润,最大利润为$122 。

示例 3

输入:customers = [3,4,0,5,1], boardingCost = 1, runningCost = 92
输出:-1
解释:

  1. 3 位游客抵达,3 位登舱,0 位等待,摩天轮轮转。当前利润为 3 * $1 - 1 * $92 = -$89 。
  2. 4 位游客抵达,4 位登舱,0 位等待,摩天轮轮转。当前利润为 is 7 * $1 - 2 * $92 = -$177 。
  3. 0 位游客抵达,0 位登舱,0 位等待,摩天轮轮转。当前利润为 7 * $1 - 3 * $92 = -$269 。
  4. 5 位游客抵达,4 位登舱,1 位等待,摩天轮轮转。当前利润为 12 * $1 - 4 * $92 = -$356 。
  5. 1 位游客抵达,2 位登舱,0 位等待,摩天轮轮转。当前利润为 13 * $1 - 5 * $92 = -$447 。
    利润永不为正,所以返回 -1 。

示例 4

输入:customers = [10,10,6,4,7], boardingCost = 3, runningCost = 8
输出:9
解释:

  1. 10 位游客抵达,4 位登舱,6 位等待,摩天轮轮转。当前利润为 4 * $3 - 1 * $8 = $4 。
  2. 10 位游客抵达,4 位登舱,12 位等待,摩天轮轮转。当前利润为 8 * $3 - 2 * $8 = $8 。
  3. 6 位游客抵达,4 位登舱,14 位等待,摩天轮轮转。当前利润为 12 * $3 - 3 * $8 = $12 。
  4. 4 位游客抵达,4 位登舱,14 位等待,摩天轮轮转。当前利润为 16 * $3 - 4 * $8 = $16 。
  5. 7 位游客抵达,4 位登舱,17 位等待,摩天轮轮转。当前利润为 20 * $3 - 5 * $8 = $20 。
  6. 4 位登舱,13 位等待,摩天轮轮转。当前利润为 24 * $3 - 6 * $8 = $24 。
  7. 4 位登舱,9 位等待,摩天轮轮转。当前利润为 28 * $3 - 7 * $8 = $28 。
  8. 4 位登舱,5 位等待,摩天轮轮转。当前利润为 32 * $3 - 8 * $8 = $32 。
  9. 4 位登舱,1 位等待,摩天轮轮转。当前利润为 36 * $3 - 9 * $8 = $36 。
  10. 1 位登舱,0 位等待,摩天轮轮转。当前利润为 37 * $3 - 10 * $8 = $31 。
    轮转 9 次得到最大利润,最大利润为 $36 。

b.分析

其实这题有点阅读理解
大概是可以模拟的 先算下复杂度
按照样例的解析那样子模拟的话
每轮走4个 一队最多来50/4轮 共有1e5个队 模拟没问题

需要有以下推论 才能知道怎样贪心才能让钱最多:

  • 最后那个免费轮次肯定不用算 因为肯定是负数的 即使加上也不会变成最大
  • 最好可以4人一队算 如果不够的时候 也不能等后面的队 因为空转一次还不如载点人 (因此我在代码中表现为一次是边入队边算 一次是剩下的一起算)
  • 只要模拟出所有中间的钱数 不用管什么时候会负收益

有了以上的话 就直接按照示例模拟就好了

总的时间复杂度为 遍历队伍的O(n)乘上每个队分批的O(m/4) m在这里上限为50

b.参考代码

class Solution {
public:
    int minOperationsMaxProfit(vector<int>& customers, int boardingCost, int runningCost) {
        int ans=-1;	//答案轮次
        int Max=0;	//最大钱
        int sum=0;	//当前钱
        int cnt=0;	//当前轮次
        queue<int> q;
        for(int i=0;i<customers.size();i++){
            q.push(customers[i]);	//入队等待人数
            int now=q.front();	//当前
            if(now>4){	//只能先运走四个
                cnt++;
                now=4;
                q.front()-=4;
                int fee=boardingCost*4-runningCost;
                sum+=fee;
                if(sum>Max){	//可以更新答案
                    Max=sum;
                    ans=cnt;
                }
            }
            else {	//当前批次不够四个
                q.pop();
                if(!q.size()){	//后面没有了
                    cnt++;
                    int fee=boardingCost*now-runningCost;
                    sum+=fee;
                    if(sum>Max){
                        Max=sum;
                        ans=cnt;
                    }
                }
                else{	//放到下个批次
                    if(cnt)q.front()+=now;
                }
            }
        }
        while(q.size()){	//不用看了 只是重复上面的 把剩下的人都处理掉而已
            int now=q.front();
            if(now>4){
                cnt++;
                now=4;
                q.front()-=4;
                int fee=boardingCost*4-runningCost;
                sum+=fee;
                if(sum>Max){
                    Max=sum;
                    ans=cnt;
                }
            }
            else {
                q.pop();
                if(!q.size()){
                    cnt++;
                    int fee=boardingCost*now-runningCost;
                    sum+=fee;
                    if(sum>Max){
                        Max=sum;
                        ans=cnt;
                    }
                }
                else{
                    if(cnt)q.front()+=now;
                }
            }
        }
        return ans;
    }
};

c.皇位继承顺序

c.题目

一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。

这个王国有一个明确规定的皇位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) ,给定一个人 x 和当前的继承顺序,该函数返回 x 的下一继承人。

Successor(x, curOrder):
    如果 x 没有孩子或者所有 x 的孩子都在 curOrder 中:
        如果 x 是国王,那么返回 null
        否则,返回 Successor(x 的父亲, curOrder)
    否则,返回 x 不在 curOrder 中最年长的孩子
比方说,假设王国由国王,他的孩子 Alice 和 Bob (Alice 比 Bob 年长)和 Alice 的孩子 Jack 组成。
  • 一开始, curOrder 为 [“king”].
  • 调用 Successor(king, curOrder) ,返回 Alice ,所以我们将 Alice 放入 curOrder 中,得到 [“king”, “Alice”] 。
  • 调用 Successor(Alice, curOrder) ,返回 Jack ,所以我们将 Jack放入 curOrder 中,得到 [“king”, “Alice”, “Jack”] 。
  • 调用 Successor(Jack, curOrder) ,返回 Bob ,所以我们将 Bob 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”, “Bob”] 。
  • 调用 Successor(Bob, curOrder) ,返回 null 。最终得到继承顺序为 [“king”, “Alice”, “Jack”, “Bob”] 。
  • 通过以上的函数,我们总是能得到一个唯一的继承顺序。

请你实现 ThroneInheritance 类:

  • ThroneInheritance(string kingName) 初始化一个 ThroneInheritance 类的对象。国王的名字作为构造函数的参数传入。
  • void birth(string parentName, string childName) 表示 parentName 新拥有了一个名为 childName 的孩子。
  • void death(string name) 表示名为 name 的人死亡。一个人的死亡不会影响 Successor 函数,也不会影响当前的继承顺序。你可以只将这个人标记为死亡状态。
  • string[] getInheritanceOrder() 返回 除去 死亡人员的当前继承顺序列表。

示例

输入
[“ThroneInheritance”, “birth”, “birth”, “birth”, “birth”, “birth”, “birth”, “getInheritanceOrder”, “death”, “getInheritanceOrder”]
[[“king”], [“king”, “andy”], [“king”, “bob”], [“king”, “catherine”], [“andy”, “matthew”], [“bob”, “alex”], [“bob”, “asha”], [null], [“bob”], [null]]
输出
[null, null, null, null, null, null, null, [“king”, “andy”, “matthew”, “bob”, “alex”, “asha”, “catherine”], null, [“king”, “andy”, “matthew”, “alex”, “asha”, “catherine”]]
解释:
ThroneInheritance t= new ThroneInheritance(“king”); // 继承顺序:king
t.birth(“king”, “andy”); // 继承顺序:king > andy
t.birth(“king”, “bob”); // 继承顺序:king > andy > bob
t.birth(“king”, “catherine”); // 继承顺序:king > andy > bob > catherine
t.birth(“andy”, “matthew”); // 继承顺序:king > andy > matthew > bob > catherine
t.birth(“bob”, “alex”); // 继承顺序:king > andy > matthew > bob > alex > catherine
t.birth(“bob”, “asha”); // 继承顺序:king > andy > matthew > bob > alex > asha > catherine
t.getInheritanceOrder(); // 返回 [“king”, “andy”, “matthew”, “bob”, “alex”, “asha”, “catherine”]
t.death(“bob”); // 继承顺序:king > andy > matthew > bob(已经去世)> alex > asha > catherine
t.getInheritanceOrder(); // 返回 [“king”, “andy”, “matthew”, “alex”, “asha”, “catherine”]

提示

  • 1 <= kingName.length, parentName.length, childName.length, name.length <= 15
  • kingName,parentName, childName 和 name 仅包含小写英文字母。
  • 所有的参数 childName 和 kingName 互不相同。
  • 所有 death 函数中的死亡名字 name 要么是国王,要么是已经出生了的人员名字。
  • 每次调用 birth(parentName, childName) 时,测试用例都保证 parentName 对应的人员是活着的。
  • 最多调用 10^5 次birth 和 death 。
  • 最多调用 10 次 getInheritanceOrder 。

c.分析

这题需要把那个继承的规律找到就行了 我原本还以为需要根据出生动态修改
这道题的本质就是树的深搜
把家庭的树画出来就行了
那么节点就一共只有1e5个
最多10次询问 最多也就1e6嘛

最后需要注意下 答案数组需要记录下死去的人 死去之后并不会影响深搜的先后顺序

总的时间复杂度为O(nq) n为节点数 q为询问次数

c.参考代码

class ThroneInheritance {
public:
    
    string king;
    
    vector<string> order;
    
    unordered_map<string,vector<string>> children;	//邻接表
    
    unordered_set<string> dead;	//记录下死人
    
    ThroneInheritance(string kingName) {
        king=kingName;
    }
    
    void birth(string parentName, string childName) {
        children[parentName].push_back(childName);
    }
    
    void death(string name) {
        dead.insert(name);
    }
    
    vector<string> getInheritanceOrder() {
        order=vector<string>();
        dfs(king);
        vector<string> ans;
        for(auto name:order)if(!dead.count(name))ans.push_back(name);
        return ans;
    }
    
    void dfs(string &x){
        order.push_back(x);
        for(auto &name:children[x])dfs(name);
    }
};

d.最多可达成的换楼请求数目

d.题目

我们有 n 栋楼,编号从 0 到 n - 1 。每栋楼有若干员工。由于现在是换楼的季节,部分员工想要换一栋楼居住。

给你一个数组 requests ,其中 requests[i] = [fromi, toi] ,表示一个员工请求从编号为 fromi 的楼搬到编号为 toi 的楼。

一开始 所有楼都是满的,所以从请求列表中选出的若干个请求是可行的需要满足 每栋楼员工净变化为 0 。意思是每栋楼 离开 的员工数目 等于 该楼 搬入 的员工数数目。比方说 n = 3 且两个员工要离开楼 0 ,一个员工要离开楼 1 ,一个员工要离开楼 2 ,如果该请求列表可行,应该要有两个员工搬入楼 0 ,一个员工搬入楼 1 ,一个员工搬入楼 2 。

请你从原请求列表中选出若干个请求,使得它们是一个可行的请求列表,并返回所有可行列表中最大请求数目。

示例 1

输入:n = 5, requests = [[0,1],[1,0],[0,1],[1,2],[2,0],[3**,4**]]
输出:5
解释:请求列表如下:
从楼 0 离开的员工为 x 和 y ,且他们都想要搬到楼 1 。
从楼 1 离开的员工为 a 和 b ,且他们分别想要搬到楼 2 和 0 。
从楼 2 离开的员工为 z ,且他想要搬到楼 0 。
从楼 3 离开的员工为 c ,且他想要搬到楼 4 。
没有员工从楼 4 离开。
我们可以让 x 和 b 交换他们的楼,以满足他们的请求。
我们可以让 y,a 和 z 三人在三栋楼间交换位置,满足他们的要求。
所以最多可以满足 5 个请求。

示例 2

输入:n = 3, requests = [[0,0],[1,2],[2,1]]
输出:3
解释:请求列表如下:
从楼 0 离开的员工为 x ,且他想要回到原来的楼 0 。
从楼 1 离开的员工为 y ,且他想要搬到楼 2 。
从楼 2 离开的员工为 z ,且他想要搬到楼 1 。
我们可以满足所有的请求。

示例 3

输入:n = 4, requests = [[0,3],[3,1],[1,2],[2,0]]
输出:4

提示

  • 1 <= n <= 20
  • 1 <= requests.length <= 16
  • requests[i].length == 2
  • 0 <= fromi, toi < n

d.分析

这题应该按照分块找最大环路的
但是因为数据量太小
很显然可以枚举出所有的选取情况 O(2^16)
然后每个情况判断是否可行 只需要遍历所有点和所有边

枚举所有情况可以用位运算压缩

判断是否可行 只需要把每条边纳入决策的时候把出度入度记录
假如可行的话 最终的度数肯定是不变的

总的时间复杂度为O(max(n,m)2^m)

d.参考代码

int in[21];
class Solution {
public:
    int maximumRequests(int n, vector<vector<int>>& requests) {
        int ans=0;
        int m=requests.size();
        for(int i=1;i<=((1<<m)-1);i++){		//枚举所有情况
            memset(in,0,sizeof(in));
            int cnt=0;
            for(int j=0;j<m;j++)	//把情况的边都纳入
                if(i&(1<<j)){
                    cnt++;
                    in[requests[j][1]]++;
                    in[requests[j][0]]--;
                }
            bool flag=true;
            for(int j=0;j<n;j++)	//判断一下是否可行
                if(in[j]){
                    flag=false;
                    break;
                }
            if(flag)ans=max(ans,cnt);
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值