Codeforces Round #703 (Div. 2) C. Guessing the Greatest

C1. Guessing the Greatest (easy version)
C2. Guessing the Greatest (hard version)

Codeforces C题Guessing the Greatest分为easy和hard两个版本,唯一区别在于对询问次数的限制,以下给出easy版本的题面

在这里插入图片描述
在这里插入图片描述
Example

input

5

3

4

output

? 1 5

? 4 5

! 1

在这里插入图片描述
题目大意

给定一个长度为n的数组,数组中的元素都不相同,每次可以询问[l,r] (l<r)区间内第二大数的位置,在限定询问次数内,找出最大数的下标位置。

分析

两个版本的基本思路都是二分,hard版本限定的询问次数更少
设答案所在的区间是[l,r],中点为mid,第二大数所在位置是smax

easy version

先询问[l,r],如果smax<=mid,再询问[l,mid],如果再次询问第二大数的位置还是smax,说明最大数所在区间为[l,mid],反之最大数所在区间为[mid+1,r],smax>mid的情况同理。这样通过两次询问就可以将答案的区间缩小一半,通过计算,最大询问次数为2*⌈log2105⌉=34

hard version

首先询问[1,n],确定第二大数的位置smax,如果smax>1,再询问[1,smax],如果第二大数的位置不变,说明最大数所在区间为[1,smax-1],否则最大数所在区间为[smax+1,n],通过不超过两次的询问确定smax和最大值所在位置的大小关系,便于下一步判断。以最大值所在区间为[1,smax-1]为例,此时l=1,r=smax-1,中点为mid,每次询问[mid,smax],如果第二大数的位置仍为smax,说明最大数在[mid,smax-1],只需让l=mid,否则r=mid-1,最大值在区间[smax+1,n]的情况是类似的。通过计算,这种方法的最大询问次数为2+⌈log2105⌉=19

AC代码

easy version

#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;
}

hard version

#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.在easy version中如果r-l==1,此时[l,r]已经是可以询问的最小区间,只需一次询问便可确定最大值的位置,这种情况单独判断
2.在hard version中,两种情况下mid的取值有所差别,mid=(l+r)>>1还是mid=(l+r+1)>>1与区间二分时l,r的取值有关

猜你喜欢

转载自blog.csdn.net/weixin_46155777/article/details/113919089
今日推荐