1. 程式人生 > 實用技巧 >leetcode 5489. 兩球之間的磁力& lg P1824 進擊的奶牛(二分)

leetcode 5489. 兩球之間的磁力& lg P1824 進擊的奶牛(二分)

題目連結:

leetcode 5489兩球之間的磁力:https://leetcode-cn.com/problems/magnetic-force-between-two-balls/

lg P1824 進擊的奶牛:https://www.luogu.com.cn/problem/P1824

這兩題都是用二分法求最小值中的最大值,說來慚愧,今天做lc5489這道題時沒有思路,閒著無聊百度了一下"求最小值中的最大值",就找到了lg這道題,然後就暢通無阻做出來了...

說到底還是題目做的少,對題型不熟悉,這兩題都是用二分列舉最大值,然後對於每一個最大值去check一下,check是用到一個小貪心思想,然後就沒了...思想比較簡單,可以看程式碼理解。

//leetcode 5489. 兩球之間的磁力
class Solution {
public:
    bool check(int dis,vector<int>& pos,int m){ //貪心的放
        int last = pos[0];m--;
        for (int i = 1; i < pos.size(); i++){
            if (pos[i]-last >= dis){
                m--;
                last = pos[i];
            }
        }
        if (m > 0) return false; //放不完就不符合條件
        return true;//反之就ok
    }
    int maxDistance(vector<int>& pos, int m) {
        sort(pos.begin(),pos.end());
        int left = 0,right = pos.back()-pos[0];
        int ans;
        while(left <= right){ //二分答案
            int mid = (left+right)/2;
            if (check(mid,pos,m)){
                ans = mid;
                left = mid+1;
               // cout<<right<<endl;
            }
            else right = mid-1;
        }
        return ans;
    }
};

//lg P1824 進擊的奶牛
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5+50;
int pos[maxn],n,c;
bool check(int x){
	int last = pos[1],now = c;
	now--;
	for (int i = 2; i <= n; i++){
		if (pos[i] - last >= x){
			last = pos[i];
			now--;
		}
	}
	if (now > 0) return false;
	return true;
}
int main()
{
	scanf("%d%d",&n,&c);
	for (int i = 1; i <= n; i++){
		scanf("%d",&pos[i]);
	}
	sort(pos+1,pos+n+1);
	int left = 0,right = pos[n]-pos[1],ans = 0;
	while(left <= right){
		int mid = (left+right)/2;
		if (check(mid)){
			ans = mid;
			left = mid+1;
			//cout<<"ans:"<<ans<<endl;
		}else right = mid-1;
	}
	
	printf("%d",ans);
	return 0;
}

最後在寫一下常用的二分模板吧,當然具體怎麼用看個人喜好和題目要求

//經過跌跌撞撞得探索,二分法的寫法有兩種
//方法1:
while(left < right){
    int mid = (left+right)/2;
    if (check(mid)) right = mid;
    else left = mid+1;
}
return left;
//用這種方法寫最終while迴圈結束的條件一定是left == right,這種寫法就是我們讓right一直佔據著答案,然後讓left不斷逼近right,最終left == right 得到right所佔據的答案

//方法2:
int ans;
while (left <= right){
    int mid = (left + right)/2;
    if (check(mid)){
        ans = mid;
        right = mid-1;
    }else left = mid+1;
}
return ans;
//這種寫法是如果我們有符合條件的答案,我們用ans來儲存,每次更新都使left和right所指的範圍是一個新的範圍

/*具體用哪一種方法要看具體題目,如果題目在二分時是:if (check(mid)) right = mid;else left = mid+1;這樣的,個人常用方法一,如果二分條件是    if (check(mid)) left = mid; else right = mid+1;這時就要用方法二了,因為如果這樣還用方法一的話會陷入死迴圈(如只有兩個數)
    具體怎麼用看個人習慣*/