LeetCode 第197场周赛 题解

前两周期末没空 没做周赛

文章目录

  • a.好数对的数目
    • a.题目
    • a.分析
    • a.参考代码
  • b.仅含 1 的子串数
    • b.题目
    • b.分析
    • b.参考代码
  • c.概率最大的路径
    • c.题目
    • c.分析
    • c.参考代码
  • d.服务中心的最佳位置
    • d.题目
    • d.分析
    • d.参考代码

a.好数对的数目

a.题目

给你一个整数数组 nums 。
如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i j ,就可以认为这是一组 好数对 。
返回好数对的数目。

示例 1
输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始

示例 2
输入:nums = [1,1,1,1]
输出:6
解释:数组中的每组数字都是好数对

示例 3
输入:nums = [1,2,3]
输出:0

提示

  • 1 = nums.length = 100
  • 1 = nums[i] = 100

a.分析

按照题目要求进行计数
对于每个i去找和他一样的j 注意ji
一样的话计数就加1

总的时间复杂度是遍历每个i和往后的j 为O(n^2)

a.参考代码

class Solution {
public
    int numIdenticalPairs(vectorint& nums) {
        int ans=0;
        for(int i=0;inums.size()-1;i++)
            for(int j=i+1;jnums.size();j++)
                if(nums[i]==nums[j])ans++;
        return ans;
    }
};

b.仅含 1 的子串数

b.题目

给你一个二进制字符串 s(仅由 ‘0’ 和 ‘1’ 组成的字符串)。
返回所有字符都为 1 的子字符串的数目。
由于答案可能很大,请你将它对 10^9 + 7 取模后返回。

示例 1
输入:s = 0110111
输出:9
解释:共有 9 个子字符串仅由 ‘1’ 组成
1 - 5 次
11 - 3 次
111 - 1 次

示例 2
输入:s = 101
输出:2
解释:子字符串 1 在 s 中共出现 2 次

示例 3
输入:s = 111111
输出:21
解释:每个子字符串都仅由 ‘1’ 组成

示例 4
输入:s = 000
输出:0

提示

  • s[i] == ‘0’ 或 s[i] == ‘1’
  • 1 = s.length = 10^5

b.分析

我们来看11011这个串 共有3+3个连续1串
为什么我会用3+3来算呢 因为我是以一个连续1的子串来计数的
没错 这就是子问题 我们再来看一个110111 这个按照我的思想就是3+6
那么这里用0来划分就搞定了

下一个问题在于怎样算出3和6呢
显然 对于一个连续的1串 他的总长为1+2+3+…+length
也就是n(n+1)2 而且这个可以用递推来加

那么总的时间复杂度就是遍历整个字符串的O(n)

b.参考代码

class Solution {
public
    int ans=0;
    const int mod = 1e9+7;	记得取模
    int numSub(string s) {
        int cnt=0;	可以说是当前的长度 用来算1+2+...+n
        for(int i=0;is.size();i++)
        {
            if(s[i]=='1'){		连续的1串
                cnt=(cnt+1)%mod;	递推增加 也可以用数列求和公式代替
                ans=(ans+cnt)%mod;
            }
            else cnt=0;		0分割开的
        }
        return ans;
    }
};

c.概率最大的路径

c.题目

给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。

示例 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCOBzdHw-1594541358065)(httpsassets.leetcode-cn.comaliyun-lc-uploaduploads202007121558_ex1.png)]
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出:0.25000
解释:从起点到终点有两条路径,其中一条的成功概率为 0.2 ,而另一条为 0.5 0.5 = 0.25

示例 2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRjf5pZZ-1594541358067)(httpsassets.leetcode-cn.comaliyun-lc-uploaduploads202007121558_ex2.png)]
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
输出:0.30000

示例 3
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mTeHREQc-1594541358069)(httpsassets.leetcode-cn.comaliyun-lc-uploaduploads202007121558_ex3.png)]
输入:n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
输出:0.00000
解释:节点 0 和 节点 2 之间不存在路径

提示

  • 2 = n = 10^4
  • 0 = start, end n
  • start != end
  • 0 = a, b n
  • a != b
  • 0 = succProb.length == edges.length = 210^4
  • 0 = succProb[i] = 1
  • 每两个节点之间最多有一条边

c.分析

看上去就是求某个条件下的最优路径
对比最短路的求法的话 单源最短路我们可以用dijkstra来做
再看下题目的复杂度 估计是要用到nlogn堆优化的dijkstra来做的

既然是类最短路模板题 我们先来思考下dijkstra正不正确

  • 假如起点的概率为1 那么刚开始的时候无路径 所以其他节点的概率应该都为0
  • 对当前所有与起点有连边节点进行计算 也就是概率乘1 这一步看起来是挺正确的
  • 考虑纳入当前新节点中概率最大的节点 这一步对不对
    • 应该是正确的 因为节点当前概率最大 乘上新的边后以dijkstra的贪心思想 概率会尽可能更大

那就直接模板干吧 详细看代码比较清晰
总的时间复杂度就是nlogn

c.参考代码

class Solution {
public
    vectorint e,ne,h;		邻接表的模板
    vectordouble w;
    int n,m;
    int idx=0;
    double maxProbability(int N, vectorvectorint& edges, vectordouble& succProb, int start, int end) {
        m=2edges.size();
        n=N;
        e.resize(m+5,0);
        w.resize(m+5,0.0);
        ne.resize(m+5,0);
        h.resize(n+5,-1);
        for(int i=0;iedges.size();i++)		先邻接表添加边
        {
            add(edges[i][0],edges[i][1],succProb[i]);
            add(edges[i][1],edges[i][0],succProb[i]);
        }
        return dijkstra(start,end);		直接跑一次迪杰斯特拉
    }
    void add(int a,int b,double W)	邻接表加边 我的博客有
    {
        e[idx]=b;
        w[idx]=W;
        ne[idx]=h[a];
        h[a]=idx++;
    }
    double dijkstra(int begin,int end)	dijk改动下 我的博客也有
    {
        vectordouble dis(n,0);	初始化为0概率 dis这个名字应该改下
        vectorint vis(n,false);
        dis[begin]=1;	只有起点是1概率
        priority_queuepairdouble,int,vectorpairdouble,int,lesspairdouble,int heap;		最大堆 因为要概率最大
        heap.push({1,begin});
        while(heap.size())
        {
            int node=heap.top().second;
            heap.pop();
            if(vis[node])continue;
            for(int i=h[node];~i;i=ne[i]){
                int now_node=e[i];double W=w[i];
                if(dis[now_node]dis[node]W)	如果可以更新
                {
                    dis[now_node]=dis[node]W;
                    heap.push({dis[now_node],now_node});
                }
            }
            vis[node]=true;
        }
        return dis[end];
    }
};

d.服务中心的最佳位置

d.题目

一家快递公司希望在新城市建立新的服务中心。公司统计了该城市所有客户在二维地图上的坐标,并希望能够以此为依据为新的服务中心选址:使服务中心 到所有客户的欧几里得距离的总和最小 。
给你一个数组 positions ,其中 positions[i] = [xi, yi] 表示第 i 个客户在二维地图上的位置,返回到所有客户的 欧几里得距离的最小总和 。
换句话说,请你为服务中心选址,该位置的坐标 [xcentre, ycentre] 需要使下面的公式取到最小值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bz47Nzx9-1594541358071)(httpsassets.leetcode-cn.comaliyun-lc-uploaduploads20200712q4_edited.jpg)]
与真实值误差在 10^-5 之内的答案将被视作正确答案。

示例 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00oCloz4-1594541358073)(httpsassets.leetcode-cn.comaliyun-lc-uploaduploads20200712q4_e1.jpg)]
输入:positions = [[0,1],[1,0],[1,2],[2,1]]
输出:4.00000
解释:如图所示,你可以选 [xcentre, ycentre] = [1, 1] 作为新中心的位置,这样一来到每个客户的距离就都是 1,所有距离之和为 4 ,这也是可以找到的最小值。

示例 2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G7ZsuyP7-1594541358075)(httpsassets.leetcode-cn.comaliyun-lc-uploaduploads20200712q4_e3.jpg)]
输入:positions = [[1,1],[3,3]]
输出:2.82843
解释:欧几里得距离可能的最小总和为 sqrt(2) + sqrt(2) = 2.82843

示例 3
输入:positions = [[1,1]]
输出:0.00000

示例 4
输入:positions = [[1,1],[0,0],[2,0]]
输出:2.73205
解释:乍一看,你可能会将中心定在 [1, 0] 并期待能够得到最小总和,但是如果选址在 [1, 0] 距离总和为 3
如果将位置选在 [1.0, 0.5773502711] ,距离总和将会变为 2.73205
当心精度问题!

示例 5
输入:positions = [[0,1],[3,2],[4,5],[7,6],[8,9],[11,1],[2,12]]
输出:32.94036
解释:你可以用 [4.3460852395, 4.9813795505] 作为新中心的位置

提示

  • 1 = positions.length = 50
  • positions[i].length == 2
  • 0 = positions[i][0], positions[i][1] = 100

d.分析

几何题 算是难的吧
毕竟不懂算法就推不出来
所以我也不分析怎么思考了 必须学过才会的
懂的就不会看我文章了 应该搜论文去了hh

这里介绍一种简单的算法 三分查找

这是因为欧氏距离有着这样的规律:
我们先看公式: ans=sum(sqrt( (x-xi)² + (y-yi)² ))
假如我们把x固定了 那么ans的最小值 就是有关y的一个二次函数的最小值
而二次函数的最小值我们是可以用三分法来找到最值的

那么同样道理 对于x 假如对应x的y都知道 那么对于每个xj而言yj是固定的 那么y可以写成关于x的函数getY(x) 那么这也是一个二次函数最值的问题 因此可以三分

所以总的就是三分套上三分

总的时间复杂度是 O(nlogmlogm) m为精度 此处精度大概为1e-8 n为50
当然 你二分个100~200次 怎么来说精度也够了

d.参考代码

class Solution {
public
    double getMinDistSum(vectorvectorint& p) {
        int n=p.size();
        auto getSum = [&n,&p](double x,double y){		获取固定(x,y)到全部点的距离之和
            double sum=0;
            for(int i=0;in;i++)
                sum+=sqrt((x-p[i][0])(x-p[i][0])+(y-p[i][1])(y-p[i][1]));
            return sum;
        };
        auto getY = [&getSum](double x){	获取固定x的y的最值
            double l=0,r=100;
            for(int i=0;i100;i++)
            {
                double m1=(l+r)2,m2=(m1+r)2;
                if(getSum(x,m1)getSum(x,m2))r=m2;
                else l=m1;
            }
            return getSum(x,l);
        };
        double l=0,r=100;
        for(int i=0;i100;i++)	可以去找下x的最值是多少
        {
            double m1=(l+r)2,m2=(m1+r)2;
            if(getY(m1)getY(m2))r=m2;	可以看到比较的是关于x的二次函数
            else l=m1;
        }
        return getY(l);
    }
};

你可能感兴趣的:(leetcode,周赛,算法,面试,leetcode,高等几何)