二分

基础知识:

  

 

 

          tips: 版本1用来求符合条件的值中下标最小的那个,版本2求符合条件中的下标最大的那个。

 

1.  最佳牛围栏

  题目链接:https://www.acwing.com/problem/content/104/ (算法竞赛进阶指南)

   思路:1. 二分的方法来枚举所有可能的平均值

      2. 检验当前的平均值是否符合条件, 如果符合条件就调大平均值,如果不符合条件就减小平均值

      3. 检验方法:求当前数组的前缀和并将每一项都减去平均值。记录所有大于等于m长度的区间的值,

       如果有区间的值大于1,说明对于该区间,当前平均值偏小,即符合条件。

   代码:

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

const int N = 100010;

int n, m;
int cows[N];
double sum[N];

bool check(double avg)
{
    for(int i = 1; i <= n; i++) 
        sum[i] = sum[i - 1] + cows[i] - avg;
        
    double minv = 0;
    for(int i = 0, j = m; j <= n; j++, i++)
    {
        //存储所有区间的最小值,因为j是从m开始枚举,
        //因此前缀区间sum[j] - sum[i]的长度一定大于m
        minv = min(minv, sum[i]);
        if(sum[j] >= minv) return true;
    }
    return false;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> cows[i];
        
    double left = 0, right = 2000;
    
    while(right - left > 1e-5)
    {
        double mid = (left + right) / 2;
        if(check(mid)) left = mid;
        else right = mid;
    }
    
    printf("%d\n", int(right * 1000));
    
    return 0;
}

 

2.特殊排序

    题目链接:https://www.acwing.com/problem/content/115/ (算法竞赛进阶指南)

   思路:

    方法1:每次取出一个数字,通过二分的方法找到这个数字在当前数组中的位置,将当前元素添加到vector最后,通过插入排序将当前元素放到正确的位置

    代码1:

class Solution {
public:
    vector<int> specialSort(int N) {
        vector<int> res;
        res.push_back(1);
        for(int i = 2; i <= N; i++)
        {
            int left = 0; int right = res.size() - 1;
            while(left < right)
            {
                int mid = left + right + 1 >> 1;
                if(compare(res[mid], i)) left = mid;
                else right = mid - 1;
            }
            
            res.push_back(i);
            for(int j = res.size() - 2; j > right; j--)
            {
                swap(res[j], res[j + 1]);
            }
            
            //排除边界条件,如果所有的数字都大于当前的数字,需要特殊判断
            if(compare(i, res[right])) swap(res[right], res[right + 1]);
        }
        
        return res;
    }
};

      方法2:归并排序

    代码2:

class Solution {
public:
    vector<int> merge(int l,int r,int N)//merge函数返回l到r区间已经排好序的vector容器
    {
        if (l == r)//递归出口,如果只有一个元素,直接把这个元素返回即可
        {
            vector<int>temp;
            temp.push_back(l);
            return temp;
        }
        int mid = (l + r) / 2;
        vector<int>a=merge(l,mid,N);//递归求解
        vector<int>b=merge(mid+1,r,N);
        vector<int>res;//res是一会要返回的容器
        int t1=0,t2=0;//t1和t2分别指向a和b容器中的元素
        int flag=0;
        while(t1<a.size()&&t2<b.size())
        {
            if(compare(a[t1],b[t2]))//如果a中元素小,就把a中元素压入res中
            {
                res.push_back(a[t1++]);
            }
            else
            {
                res.push_back(b[t2++]);
            }
            if(t1>=a.size())//如果t1已经指向了a容器末尾,flag=1表示a已经遍历完全
            {
              flag=1;break;  
            }
            if(t2>=b.size())//同理
            {
                flag=2;break;
            }
        }
        if(flag==1)//把b中剩余元素压入res中
        {
            for(int i=t2;i<b.size();i++)
            {
                res.push_back(b[i]);
            }
        }
        else//同理
        {
            for(int i=t1;i<a.size();i++)
            {
                res.push_back(a[i]);
            }
        }
        return res;
    }
    vector<int> specialSort(int N) {
        vector<int>ans=merge(1,N,N);
        return ans;
    }
};

 

3.Kth Smallest Number in Multiplication Table

  题目链接:https://leetcode.com/problems/kth-smallest-number-in-multiplication-table/ (leetcode)

  思路:https://www.acwing.com/solution/content/524/

  代码:

class Solution {
public:
    int check(int x, int m, int n) {
        int tot = 0;
        for (int i = 1; i <= m; i++)
            tot += min(n, x / i);
        return tot;
    }
    int findKthNumber(int m, int n, int k) {
        int l = 1, r = n * m;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (k <= check(mid, m, n))
                r = mid;
            else
                l = mid + 1;
        }
        return l;
    }
};

 

4.Dungeon Game

  题目链接:https://leetcode.com/problems/dungeon-game/ (leetcode)

  思路:对可能的答案进行二分,检验每一个可能出现的值,验证它的正确性以确定二分的移动方向,验证的时候确保当前初始值情况下走和最大的路径是否可以达到终点

  代码:

class Solution {
public:
    bool check(int initial, vector<vector<int>>& dungeon) {
        int m = dungeon.size(), n = dungeon[0].size();
        vector<vector<int>> f(m, vector<int>(n, 0));

        f[0][0] = initial + dungeon[0][0];
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++) {
                if (i > 0 && f[i - 1][j] > 0)
                    f[i][j] = max(f[i][j], f[i - 1][j] + dungeon[i][j]);

                if (j > 0 && f[i][j - 1] > 0)
                    f[i][j] = max(f[i][j], f[i][j - 1] + dungeon[i][j]);
            }

        return f[m - 1][n - 1] > 0;
    }

    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        const int INF = 1000000000;
        int l = 1, r = INF;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (check(mid, dungeon)) r = mid;
            else l = mid + 1;
        }

        return l;
    }
};

 

posted @ 2020-08-10 08:58  锤子科技未来产品经理  阅读(144)  评论(0)    收藏  举报