【LeetCode 周赛题解】第340场周赛题解——二分、优化BFS

【题目列表】

对角线上的质数
等值距离和
最小化数对的最大差值
网络图中最少访问的格子数


对角线上的质数

  遍历对角线上的数,维护最大质数就行了。

class Solution {
public:
    bool isprime(int n){
        if(n==1)return false;
        if(n==2)return true;
        for(int i=2;i<=sqrt(n);i++){
            if(n%i==0)return false;
        }
        return true;
    }
    int diagonalPrime(vector<vector<int>>& nums) {
        int res=0;
        for(int i=0;i<nums.size();i++){
            if(isprime(nums[i][i])){
                res=max(res,nums[i][i]);
            }
            if(isprime(nums[i][nums.size() - i - 1])){
                res=max(res,nums[i][nums.size() - i - 1]);
            }
        }
        return res;
    }
};

等值距离和

  假设当前数为 n u m s [ i ] = 7 nums[i]=7 nums[i]=7 ,当前 7 7 7 与其左边的 7 7 7 的距离和为 d i s [ i ] dis[i] dis[i] ,前缀数组中存在 a n s ans ans 7 7 7 且上一个 7 7 7 的位置为 i d x idx idx ,则前缀中的 7 7 7 对当前 7 7 7 的贡献值为 d i s [ i d x ] + a n s ∗ ( i − i d x ) dis[idx]+ans*(i-idx) dis[idx]+ans(iidx)
这只是计算了前缀,所以我们再从后往前跑一遍,每个位置上前缀贡献和后缀贡献的和即为最终的答案。

class Solution {
public:
    vector<long long> distance(vector<int>& nums) {
        int n=nums.size();
        vector<long long> res(n,0);
        vector<long long> cnt(n,0);
        map<int,int> idx;
        map<int,int> ans;
        for(auto num:nums)idx[num]=-1,ans[num]=0;
        for(int i=0;i<n;i++){
        //其实这里可以不用绝对值函数,比赛时间紧懒得改了
            if(idx[nums[i]]!=-1) res[i]=res[idx[nums[i]]]+ans[nums[i]]*abs(i-idx[nums[i]]);
            idx[nums[i]]=i;
            ans[nums[i]]++;
        }
        for(auto num:res)cout<<num<<" ";
        for(auto num:nums)idx[num]=-1,ans[num]=0;
        for(int i=n-1;i>=0;i--){
            if(idx[nums[i]]!=-1) cnt[i]=cnt[idx[nums[i]]]+ans[nums[i]]*abs(i-idx[nums[i]]);
            idx[nums[i]]=i;
            ans[nums[i]]++;
            res[i]+=cnt[i];
        }
        cout<<endl;
        for(auto num:res)cout<<num<<" ";
        return res;
    }
};

最小化数对的最大差值

  首先不可能在原数组里面枚举,所以排序是肯定的,排序会更方便计算最小的差值。然后就是一个特点:最优解里的每一对数对一定是相邻的。可以反证得到,如果选择的数对之间隔了一个和左右两个数都不相等的数,那中间那个数和区间以外的数组成一对,就会造成重复区间。如果存在连续相同的数,如 [ 1 , 2 , 2 , 2 , 4 ] [1,2,2,2,4] [1,2,2,2,4] ,可以发现,存在不相邻的选择方法,但是和相邻的方法最终结果相等,最优数对之间依然是相邻的。但是存在这样一个问题,对于头尾的数,每个数都可以选择与其左边的或右边的数组成一队,我们也没法在前面就确定当前组合对于后续来说一定是最优解。
  别人都说看到最大最小值,最小最大值都应该首先想到二分。因为这个题只能反推,我们假设数对差值的最大值是 X X X,我们可以用二分的方法查找可能的 X X X ,并统计差值小于这个值的数对的数量,如果这个数量等于 p p p,则 X X X 即为满足条件的结果。

class Solution {
public:
    int minimizeMax(vector<int>& nums, int p) {
        if(p==0) return 0;
        int n=nums.size();
        sort(nums.begin(),nums.end());
        // for(auto num:nums)cout<<num<<" ";
        // cout<<endl;
        auto check=[&](int limit){
            int ans=0;
            for(int i=1;i<n;i++) if(nums[i]-nums[i-1]<=limit) ans++,i++;
            return ans>=p;
        };

        int l=0,r=(nums[n-1]-nums[0]);
        while(l<r){
            int mid=(l+r)/2;
            if(check(mid)) r=mid;//数量大于p说明当前X太大了
            else l=mid+1;
        }
        return l;
    }
};

网络图中最少访问的格子数

我之前没做过记忆化BFS,觉得还挺新奇的,竟然每次很多走法的题也能用BFS做。后来想想,其实我们每一步都尽最大可能走,然后步长又是逐渐增大的,所以我们能保证每个格子第一次被访问,都是最小步长走到的。很amazing。

class Solution {
public:
    int minimumVisitedCells(vector<vector<int>>& grid) {
        int m=grid.size(),n=grid[0].size();
        vector<vector<bool>> vis(m,vector<bool> (n,false));
        queue<pair<int,int>> q;
        q.push({0,0});
        vis[0][0]=true;
        int res=0;

        while(!q.empty()){
            res++;
            int ans=q.size();
            for(int i=0;i<ans;i++){
                auto [r,c]=q.front();
                q.pop();
                if(r==m-1&&c==n-1) return res;
                int k=grid[r][c];
                if(k==0)continue;//寸步难行
                if(r+k>=m-1&&c==n-1) return res+1;//下一步就能走到终点
                if(r==m-1&&c+k>=n-1) return res+1;
                for(int j=c+1;j<n&&j<=k+c;j++){//向右走
                    if(!vis[r][j]){
                        q.push({r,j});
                        vis[r][j]=true;
                    }
                }
                for(int j=r+1;j<m&&j<=r+k;j++){//向下走
                    if(!vis[j][c]){
                        q.push({j,c});
                        vis[j][c]=true;
                    }
                }
            }
        }
        return -1;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值