笔试模拟 day5

观前提醒:

笔试所有系列文章均是记录本人的笔试题思路与代码,从中得到的启发和从别人题解的学习到的地方,所以关于题目的解答,只是以本人能读懂为目标,如果大家觉得看不懂,那是正常的。如果对本文的某些知识有不同的观点,欢迎讨论。

题目链接:

第一题:孩子们的游戏(圆圈中最后剩下的数)_牛客题霸_牛客网

第二题:腐烂的苹果_牛客题霸_牛客网

第三题:游游的you__牛客网

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

第一题 (重做)

思路:

本题是一道经典的约瑟夫环问题,引申出的解法思路有两种

1)一是借助STL的vector与list模拟圆圈循环,不断模拟下去,一个个删除元素,最后的就是想要的那个孩子。

这里建议使用链表模拟,因为vector的删除节点还是有点费事的。不过关于vector解法也有相应的优化策略,例如可以额外创建一个表,专门记录这个节点有没有删除。

2)利用动态规划,迭代等,核心:找到规律,只要找到规律使用那个方法都可以

我们观察下图,如果说黑笔是第一次循环,在我们第一次将某个值排除后,那我们是不是可以将排除位置后的那个看成新的起点,那我们是不是可以将蓝色笔记看成第二次查找。

但是我们既然将“5”看成新的起点,那我们是不是可以把第二次查找看成是n-1个孩子的查找问题,并且n-1与n个孩子的查找结果应该是一样的 

对不对,那我们是不是就找到一种符合动态规划的关系,那么我们就可以使用动态规划解决问题。

那我们接下来的问题是找到dp[n-1]与dp[n]的关系。

在上面的分析中,我们意识到两种问题的结果是一样的,我们要找到的是两种情况的下标,

因为我们的下标在外面设置新起点的时候就已经改变了。

我们开始分析下标的映射

对应黑蓝两种情况,

【m,0】,【m+1,1】,【n-3,n-3-m】,【n-2-m】,【n-1-m】,【】(零点是一个重要节点按照前面的递推,我们是吧它映射为0-m吗?

(不是,我们要记住它是一个环,按照环的推理,0点也是n点,1点也是n+1点,所以从“0”后的点都要加上 n)

所以经过上面的讨论,我们是不是发现了两种情况的下标映射关系,

dp[n]=dp[n-1]+m,想一想对不对,是不是我们没有考虑到成环的0点后面的问题,

所以我们要减去一个n,对不对再加分类讨论有没有过0点,那岂不是太麻烦了

我们想一想在数组中是如何模拟成环的,模上一个数组大小不就好了,还不用分类讨论。

所以我们最后关系如下

dp[n]=(dp[n-1]+m)%n【由于不断在删除数据,所以n的值是在减少的】;

代码:

//解法一,使用数组模拟解决
class Solution {
public:
 
    int LastRemaining_Solution(int n, int m) {
        vector<int> nums(n);
        int i=0;
        for(int i=0;i<n;i++) nums[i]=i;
        while( nums.size() != 1)
        {
            int count=m;
            while(--count) 
            {
                i++;
                i%=nums.size();
            }
            nums.erase(nums.begin()+i,nums.begin()+i+1);
        }
        return nums[0];
    }
};

//解法二,找规律
class Solution {
public:
 
    int LastRemaining_Solution(int n, int m) {
        if (n == 0 || m == 0) return -1;
        //实际上我们根本没有必要专门开辟一个dp表,
        //我们发现dp[n]只会使用dp[n-1]一种状态值我们使用单个变量记录一下就好
        int f=0;
        for(int i=2;i<=n;i++)
        f=(f+m)%i;
        return f;
    }
};

 

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

第二题 

思路:

一道多源dfs,大家只要记住模板了,解决就不难了。

需要注意的是,我们记录的时间,但是在dfs中我们只能记录下层数,并且层数还会多一层,那是我们最后感染完了,但是还要向外遍历一遍,判断有没有要感染的。

代码:

class Solution {
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    int m=0,n=0;
public:
    int rotApple(vector<vector<int> >& grid) {
       int count=0;
        queue<pair<int,int>> dp;
        m=grid.size(),n=grid[0].size();
        int level=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j] == 1)
                count++;
                if(grid[i][j] == 2) dp.push({i,j});
            }
        }
        while(dp.size())
        {
            // cout<<"ss"<<endl;
            int z=dp.size();
            for(int j=0;j<z;j++)
            {
                auto& [a,b]=dp.front();
                dp.pop();
                for(int i=0;i<4;i++)
                {
                    // cout<<"ss"<<endl;
                    int x=dx[i]+a,y=dy[i]+b;
                    // cout<<x<<":"<<y<<endl;
                    if(x >=0 && x < m && y >= 0 && y < n && grid[x][y] == 1)
                    {
                        grid[x][y]=2;
                        count--;
                        dp.push({x,y});
                    }
                }
            }
            level++;
        }
        // printf("%d\n",count);
        return count>0 ? -1:level-1;
    }
};

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

---------------------------------------------------我是分割线---------------------------------------------------------------

第三题 

思路:

一道简单的贪心,要想分数最大那我们首先要组成最多的“you”,剩下的“o”排在一起组成最多的“oo”组合,值得注意的是,“ooo”是算作两对“oo”计两分,所以我们有n个o,就最多有n-1对“oo”。

代码:

#include <iostream>
#include <cmath>
using namespace std;

int main() {
   int n=0;
   cin>>n;
    for(int i=0;i<n;i++)
    {
        int count=0;
        int a=0,b=0,c=0;
        cin>>a>>b>>c;
        int x=min(a,min(b,c));
        count=x*2;
        a-=x,b-=x,c-=x;
        if(b)
        {
            count+=(b-1);
        }
        printf("%d\n",count);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值