ブルーブリッジカップエッセンシャルアルゴリズム2:二分探索

二分探索

[次のプログラムはすべて、区間[l、r]でxを検索し、デフォルトのデータ順序は減少しません]

1.二分探索間隔での数値の添え字

二分探索間隔(存在し、一意)内の数値の添え字が存在しない場合は、-1を返します。

int search(int l,int r,int x)
{
    
    
	int mid;
	while (l<=r)
	{
    
    
		mid=(l+r)>>1;
		if(a[mid]==x) return mid;
		if(a[mid]<x) l=mid+1;
		else r=mid-1;
	}
	return -1;
}

これは最もよく書かれた二分法でなければなりません

2.クエリ間隔での<=xの最大値

間隔内の<=xの最大値を照会します(複数の最大値がある場合は、右端の座標を返します)。

int search(int l,int r,int x)
{
    
    
	int mid;
	while (l<r)
	{
    
    
		mid=(l+r+1)>>1;
		if(a[mid]<=x) l=mid;
		else r=mid-1;
	}
	return l;
}

2点プロセスについてはあまり言うことはありませんが、2点に注意してください。

  1. [l、r]でクエリを実行する場合、渡される推奨パラメーターはl-1.rです。したがって、戻り値がl-1の場合、値<= xがないことを意味します。つまり、 2つのポイントの前に判断することを選択できます。解決策が存在すると判断されたら、再度分割します。
  2. mid =(l + r)>>1の代わりにmid=(l + r + 1)>> 1後者の方法を採用する場合、間隔が2、3、次にmid = 2に短縮されたとすると、 [mid] <= xが確立され、次にl = midが確立されます。これは無限ループになるため、+1は2で除算されます。

3.クエリ間隔の>=xの最小値

間隔内の最小値>=xを照会します(複数の最小値がある場合は左端の座標を返します)。

int search(int l,int r,int x)
{
    
    
	int mid;
	while (l<r)
	{
    
    
		mid=(l+r)>>1;
		if(a[mid]>=x) r=mid;
		else l=mid+1;
	}
	return l;
}

条件がr=midおよびl=mid + 1になっただけなので、midをmid =(l + r)>> 1に変更する必要があります。そうしないと、無限ループになります。クエリ間隔[l、r]はパラメータl、r + 1として渡され、戻り値は解がないためr+1です。
このように、上限と下限、および中間を取る条件が適切に制御されている限り、2つに分けることができます。

4.実数を除算します

実数の二分法は書きやすいです。トピックに必要な精度に従って境界を制御するだけです。通常、無限ループはありません。

while (fabs(r-l)>EPS)//EPS是题目要求的精度
{
    
    
	mid=(l+r)/2;
	if(check(mid)) l=mid;
	else r=mid;
}

5、練習用の質問

1.ネットワークケーブルスーパーバイザー

トピックの説明と入力および出力

ここに画像の説明を挿入
入出力:

输入:
4 11
8.02
7.43
4.57
5.39
输出:
2.00

トピックのアイデアとコード

アイデア:これは、2つのポイントに直接分割して、結果と一致するかどうかを判断することです。特殊な場合、つまり分割できない場合に注意して判断してください。
コード:

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const ll maxn=1e5+10;
const ll maxm=1e8+10;
const ll mod=1e9+7;
ll n,m;
ll a[maxn];
ll mx;
ll check(ll x){
    
    
    ll res=0;
    for(int i=1;i<=n;i++){
    
    
        res+=a[i]/x;
    }
    return res;
}
int main(){
    
    
    cin>>n>>m;
    ll sum=0;
    for(int i=1;i<=n;i++){
    
    
        double s;
        cin>>s;
        a[i]=s*100;
        mx=max(a[i],mx);
        sum+=a[i];
    }
    
    if(m>sum){
    
    
        printf("0.00");
        return 0;
    }
    ll l=1,r=mx;
    ll ans=0;
    while(l<r){
    
    
        ll mid=(l+r+1)>>1;
        if(check(mid)>=m){
    
    
            l=mid;
        }
        else{
    
    
            r=mid-1;
        }

    }
    printf("%.2lf",l*1.0/100);
	return 0;
}

2.教室を借りる

トピックの説明と入力および出力

ここに画像の説明を挿入

入出力:

输入:
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出:
-1
2

トピックのアイデアとコード

アイデア: 1つ目は区間の修正です。ここでは、差を使用して問題を解決し、結果の区間を二分法で狭めます。
二分コードと差動コード

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const ll maxn=1e5+10;
const ll maxm=1e8+10;
const ll mod=1e9+7;
int line[1000010], l[1000010], r[1000010], d[1000010], change[1000010],sum[1000010];
int n,m;
int check(int x)
{
    
    
	memset(change,0,sizeof(change));

	for (int i = 1; i <= x; i++)
	{
    
    
        change[l[i]]+=d[i];
        change[r[i]+1]-=d[i];
	}

	for (int i = 1; i <= n; i++)
		sum[i]=sum[i-1]+change[i];
		//obj 2抹平差分数组

	for (int i = 1; i <= n; i++)
		if(sum[i]>line[i])return false;
		//obj 3什么情况下是发生了问题?

	return true;
}
int main(){
    
    
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &line[i]);
	for (int i = 1; i <= m; i++)
		scanf("%d %d %d", &d[i], &l[i], &r[i]);

	if(check(n))
	{
    
    
		printf("0");
		return 0;
	}

	int l = 1, r = n, mid=0;
	while(l<r)
	{
    
    
		mid = (l + r) >> 1;
		if(!check(mid))//obj 5
			r=mid;
		else
			l=mid+1;
        //cout<<l<<" "<<r<<" "<<check(mid)<<endl;
	}
	printf("-1\n");
	printf("%d", l);

	return 0;
}

ツリー配列コード:

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const ll maxn=1e5+10;
const ll maxm=1e8+10;
const ll mod=1e9+7;
int line[1000010], l[1000010], r[1000010], d[1000010], t[1000010],ti[1000010];
int n,m;
int lowbit(int n){
    
    
    return n&(-n);
}
void update(int x,int val){
    
    
    for(int i=x;i<=n;i+=lowbit(i)){
    
    
        t[i]+=val;
        ti[i]+=val*x;
    }
}
int getsum(int x){
    
    
    int sum=0;
    for(int i=x;i;i-=lowbit(i)){
    
    
        sum+=(x+1)*t[i]-ti[i];
    }
    return sum;
}
int pan(){
    
    
    int ans=0;
    for(int i=1;i<=n;i++){
    
    
        int sum=getsum(i)-getsum(i-1);
        if(sum>line[i])return 0;
    }
    return 1;
}
int main(){
    
    
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &line[i]);
    int f=0;
	for (int i = 1; i <= m; i++)
    {
    
    
        scanf("%d %d %d", &d[i], &l[i], &r[i]);
        update(l[i],d[i]);
        update(r[i]+1,-d[i]);
        if(f)continue;
        if(!pan()){
    
    
            f=i;
        }
    }
    if(f)printf("-1\n%lld",f);
    else{
    
    
        printf("0");
    }
	return 0;
}

6、みんなへの推薦の段落


「未定の場合は春のそよ風に聞いてみてください。春のそよ風が話せない場合は心に従います」とは、何かに躊躇している場合は、春のそよ風にどうやってやるのか聞いてみてください。「未定の場合は春のそよ風を聞くことができます。春のそよ風が話せない場合は心に従います。」この文章はインターネット作家「風水オペラ王子」が書いた「建来」からのものです。原文は、「未定の場合は、春のそよ風を聞くことができます。あなたの心に従ってください」です。

ここに画像の説明を挿入


おすすめ

転載: blog.csdn.net/weixin_46627433/article/details/123538537