【leetcode】周赛197---(1)1512. 好数对的数目(2)1513. 仅含 1 的子串数(3)1514. 概率最大的路径(4)1515. 服务中心的最佳位置

1512、给你一个整数数组 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 开始

 map映射每个key对应的个数,用n(n-1)/2得出每个key可能的排列数。

class Solution {
public:
    int numIdenticalPairs(vector<int>& nums) {
        map<int, int> mp;
        for(int i= 0; i < nums.size(); i++)
            mp[nums[i]]++;
        int ans = 0;
        for(auto it = mp.begin(); it != mp.end(); it++)
            ans += (it->second * (it->second - 1) / 2);
        return ans;
    }
};

 结果:

执行用时:4 ms, 在所有 C++ 提交中击败了53.10% 的用户

内存消耗:7.7 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

修改:减少一次循环。遇到一个数key,与在其之前的每一个数mp[key]均构成组合,ans += mp[key]++;

class Solution {
public:
    int numIdenticalPairs(vector<int>& nums) {
        map<int, int> mp;
        int ans = 0;
        for(int i= 0; i < nums.size(); i++)
            ans += mp[nums[i]]++;
        return ans;
    }
};

结果:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户

内存消耗:7.6 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

1513、给你一个二进制字符串 s(仅由 '0' 和 '1' 组成的字符串)。

返回所有字符都为 1 的子字符串的数目。

由于答案可能很大,请你将它对 10^9 + 7 取模后返回。

示例 1:

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

 遍历找仅含1的子序,其长度为n,则组合数为n(n+1)/2。注意mod,直接将数据类型设成long long比较稳妥,不然会报错。

class Solution {
public:
    int MOD = 1e9 + 7;
    int numSub(string s) {
        long long cnt = 0, ans = 0;
        for(int i = 0; i < s.size(); i++) {
            if(s[i] == '0' && cnt > 0){
                ans += ((cnt + 1) * cnt / 2) % MOD;
                cnt = 0;
            }
            if(s[i] == '1') cnt++;
        }
        ans += ((cnt + 1) * cnt / 2) % MOD;
        return ans;
    }
};

结果:

执行用时:36 ms, 在所有 C++ 提交中击败了76.23% 的用户

内存消耗:8.8 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

1514、给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。

指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。

如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。

示例1:

输入: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

 使用dfs,对每一个边进行遍历,直至遇到end,取当前的权值积res与历史权重积ans最值,ans=max(ans,res)。但是会超时,

9 / 16 个通过测试用例。

struct E{
    int v;
    double c;
};

class Solution {
public:
    vector<vector<E>> e;
    vector<bool> mark;
    double ans;
    void dfs(int x, int end, double res){
        if(x == end) {
            ans = max(ans, res);
            return;
        }
        for(int i = 0; i < e[x].size(); i++){
            int next = e[x][i].v;
            double cost = e[x][i].c;
            if(!mark[next]){
                mark[next] = true;
                dfs(next, end, res * cost);
                mark[next] = false;
            }
        }
    }

    double maxProbability(int n, vector<vector<int>>& edges, vector<double>& w, int start, int end) {
        //邻接链表存图
        e.resize(n);
        mark.resize(n, false);
        E tmp;
        for(int i = 0; i < edges.size(); i++){
            tmp.c = w[i];
            tmp.v = edges[i][0];
            e[edges[i][1]].push_back(tmp);
            tmp.v = edges[i][1];
            e[edges[i][0]].push_back(tmp);
        }
        mark[start] = true;
        ans = 0.0;
        dfs(start, end, 1.0);
        return ans;
    }
};

修改:我自己想出来的~用BFS,从cur点,每找一个相邻点next,判断此时到next的概率是否为最大,res[next]存到next的概率,初始化为0,当res[next] < res[cur] * cost,证明此时到next的最大概率更新了,需要重新入队列,更新与next相连的点的概率!!

struct E{
    int v;
    double c;
};

class Solution {
public:
    vector<vector<E>> e;

    double maxProbability(int n, vector<vector<int>>& edges, vector<double>& w, int start, int end) {
        //邻接链表存图
        e.resize(n);
        E tmp;
        for(int i = 0; i < edges.size(); i++){
            tmp.c = w[i];
            tmp.v = edges[i][0];
            e[edges[i][1]].push_back(tmp);
            tmp.v = edges[i][1];
            e[edges[i][0]].push_back(tmp);
        }

        // BFS
        vector<double> res(n, 0.0); // 到i点的最大概率值
        queue<int> q;
        q.push(start);
        res[start] = 1.0;
        while(!q.empty()){
            int cur = q.front();
            q.pop();
            for(int i = 0; i < e[cur].size(); i++){
                int next = e[cur][i].v;
                double cost = e[cur][i].c;
                if(res[next] < res[cur] * cost){
                    res[next] = res[cur] * cost;
                    q.push(next);
                } 
            }
        }
        return res[end];
    }
};

结果:

执行用时:408 ms, 在所有 C++ 提交中击败了88.24% 的用户

内存消耗:65.9 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

三分查找

1515、一家快递公司希望在新城市建立新的服务中心。公司统计了该城市所有客户在二维地图上的坐标,并希望能够以此为依据为新的服务中心选址:使服务中心 到所有客户的欧几里得距离的总和最小 。

给你一个数组 positions ,其中 positions[i] = [xi, yi] 表示第 i 个客户在二维地图上的位置,返回到所有客户的 欧几里得距离的最小总和 。

换句话说,请你为服务中心选址,该位置的坐标 [xcentre, ycentre] 需要使下面的公式取到最小值:

与真实值误差在 10^-5 之内的答案将被视作正确答案。

示例 1:

输入:positions = [[0,1],[1,0],[1,2],[2,1]]
输出:4.00000
解释:如图所示,你可以选 [xcentre, ycentre] = [1, 1] 作为新中心的位置,这样一来到每个客户的距离就都是 1,所有距离之和为 4 ,这也是可以找到的最小值。

三分查找:一个 R上的凸函数 y=f(x),定义域为 [L,R],用三分查找的方式将不可能包含最小值点的范围排除:

(1)取两个点 x1,x2,满足 L<x1<x2<R,记 y1=f(x1),y2=f(x2);

(2)如果 y1>y2​,那么最小值点一定不在 [L,x1] 内;如果 y1<y2,那么最小值点一定不在 [x2,R]内。

 

题目中的函数 f(xc,yc)是二元函数,我们可以使用「三分查找」套「三分查找」的方法。外层的三分查找用来确定 xc的范围,每次三分查找时,取定义域 [xL,xR]中的两个三等分点 x1,x2​,并分别固定 xc的值为 x1和 x2,对 yc​ 进行三分查找。

取 x1,x2​ 为 [L,R] 的三等分点,即:

每次就可以排除三分之一的定义域。当定义域的宽度 R−L 收敛至给定的阈值 ϵ时,就可以结束三分查找。此时 L 和 R 足够接近,可以将 [L,R]内的所有值都视作极值点。

 // 此处不加&,会超时
  for(auto &p:pos)

const double EPS = 5e-6;    // 误差阈值,视left和left + EPS有相同的性质
const double INF = 1e9;

class Solution {
public:
    double dist(double x, double y, vector<vector<int>>& pos){
        double ans = 0;
         // 此处不加&,会超时
        for(auto &p:pos)   // 对vector使用迭代器遍历,注意&p,有一个引用号&
            ans += sqrt((x - p[0]) * (x - p[0]) + (y - p[1]) * (y - p[1]));
        return ans;
    }

    // 确定x值,对y方向三分查找最值
    double caly(double x, vector<vector<int>>& pos){
         double ans = INF;

        int l = pos[0][1], r = pos[0][1];
        // 此处不加&,会超时
        for(auto &p:pos) l = min(l, p[1]), r = max(r, p[1]);
        double left = l - EPS, right = r + EPS;
       
        while(left + EPS < right){
            double y1 = left + (right - left) / 3.0;
            double y2 = right - (right - left) / 3.0;
            
            double ans1 = dist(x, y1, pos);
            double ans2 = dist(x, y2, pos);
        
            ans = min(ans, min(ans1, ans2));
            
            if(ans1 > ans2) left = y1;
            else right = y2;
        }
        return ans;
    }

    double getMinDistSum(vector<vector<int>>& pos) {
        double ans = INF;
        int l = pos[0][0], r = pos[0][0];
        // 找x取值范围[left, right]  // 此处不加&,会超时
        for(auto &p:pos) l = min(l, p[0]), r = max(r, p[0]); // min(double, int)没有这样的函数,只能比较int--> min(int,int)
        double left = l - EPS, right = r + EPS;
        
        while(left + EPS < right){
            double x1 = left + (right - left) / 3.0;
            double x2 = right - (right - left) / 3.0;

            double y1min = caly(x1, pos);
            double y2min = caly(x2, pos);
            
            ans = min(ans, min(y1min, y2min));

            if(y1min < y2min) right = x2;
            else left = x1;
        }
        return ans;
    }
};

结果:

执行用时:268 ms, 在所有 C++ 提交中击败了41.47% 的用户

内存消耗:8 MB, 在所有 C++ 提交中击败了100.00%

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值