Codeforces Round#703(Div。2)C。最大のものを推測する

C1。最高の(簡単なバージョン)C2を推測し
ます。最高のものを推測する(ハードバージョン)

Codeforces Cの質問Guesingthe Greatestは、簡単と難しいの2つのバージョンに分けられます。唯一の違いは、問い合わせの数の制限です。簡単なバージョンを以下に示します。

ここに画像の説明を挿入
ここに画像の説明を挿入

入力

5

3

4

出力

1 5

4 5

1

ここに画像の説明を挿入
一般的なアイデア

長さnの配列が与えられると、配列内の要素は異なります。区間[l、r](l <r)で2番目に大きい数の位置を照会し、制限された数の中で最大の数を見つけることができるたびにクエリの数添え字の位置。

分析

2つのバージョンの基本的な考え方は二分法であり、ハードバージョンは問い合わせの数を制限しません。
回答範囲を[l、r]、中点を中点、2番目に大きい数をsmaxとします。

簡単版

最初に[l、r]に尋ね、smax <= midの場合、次に[l、mid]に尋ねます。もう一度尋ねると、2番目に大きい数の位置がまだsmaxである場合、最大数の間隔は[l、 mid]、およびその逆。間隔は[mid + 1、r]であり、smax> midは同じです。このように、応答間隔が倍尋ねることによって半分に減少させることができる。計算により、問い合わせの最大数は、2 *⌈logある2 10 5= 34

ハードバージョン

最初のクエリ[1、n]で2番目に大きい数の位置smaxを決定し、smax> 1の場合、クエリ[1、smax]、2番目に大きい数の位置が変わらない場合、最大数の間隔は[1、smax] -1]、それ以外の場合、最大数の間隔は[smax + 1、n]です。2つ以下のクエリでsmaxと最大値の位置の間のサイズの関係を決定します。これは、判断の次のステップ。最大値[1、smax-1]の間隔を例にとると、この時点ではl = 1、r = smax-1であり、中点は中央です。[mid、smax]に尋ねるたびに、位置が2番目に大きい数のはまだsmaxです。これは、最大数が[mid、smax-1]にあることを意味し、l = mid、それ以外の場合はr = mid-1とし、最大値は[smax + 1、n]。状況は似ています。計算により、この方法でクエリの最大数は、2 +⌈logある2 10 5= 19

ACコード

簡単版

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

int ask(int l,int r)
{
    
    
	if(l>=r) return 0;
	int x;
	cout<<"? "<<l<<" "<<r<<endl;
	cin>>x;
	return x;
}

int main()
{
    
    
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
	int l=1,r=n;
	int x,y;
	while(l<r)
	{
    
    
		if(r-l==1)
		{
    
    
			x=ask(l,r);
			if(x==l) l=r;
			break;
		}
		int mid=(l+r)>>1;
		x=ask(l,r);
		if(x<=mid)
		{
    
    
			y=ask(l,mid);
			if(y==x) r=mid;
			else l=mid+1;
		}
		else
		{
    
    
			y=ask(mid+1,r);
			if(y==x) l=mid+1;
			else r=mid;
		}
	}
	cout<<"! "<<l<<endl;
	
	return 0;
}

ハードバージョン

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

int ask(int l,int r)
{
    
    
	int num;
	cout<<"? "<<l<<" "<<r<<endl;
	cin>>num;
	return num;
}

int main()
{
    
    
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
	int x,y;
	int l,r;
	x=ask(1,n);
	if(x!=1) y=ask(1,x);
	if(y!=x||x==1)
	{
    
    
		l=x+1,r=n;
		while(l<r)
		{
    
    
			int mid=(l+r)>>1;
			if(ask(x,mid)!=x) l=mid+1;
			else r=mid;
		}
	}
	else
	{
    
    
		l=1,r=x-1;
		while(l<r)
		{
    
    
			int mid=(l+r+1)>>1;
			if(ask(mid,x)!=x) r=mid-1;
			else l=mid;
		}
	}
	cout<<"! "<<l<<endl;
	
	return 0;
}

注意

1.イージーバージョンでは、rl == 1の場合、[l、r]はすでに照会可能な最小間隔であり、最大値の位置は1回の照会で決定できます。この状況は個別に判断されます
2。ハードバージョンでは、2つの場合のmidの値が異なり、mid =(l + r)>> 1またはmid =(l + r + 1)>> 1は、のl、rの値に関連しています。区間二分法

おすすめ

転載: blog.csdn.net/weixin_46155777/article/details/113919089