【蓝桥杯有点意思】区间移位 二分答案

题目链接戳这里

前言

蓝桥杯的题目真有意思。
题目比较实际化,所以这个题目的难度也就一般般,因为动手画画就能画出来。这个题是B组决赛最后一题吧。总感觉省赛的题目比较难


正文

这个题目的问法就很二分,我们直接二分起来。
check()中有贪心的成分,所以算法的正确性我确实证明不出来。然后他可以AC啊

直观上看,我们按右端点排序,然后移动到比较靠近没有覆盖的地方,就可以了。这样因为是有序的区间,所以移动的距离也是最小的。

我们怎么check()呢?
我先放上check()的代码,然后逐条解读:

bool check(int x)
{
	int k = 0;
	vector<node> tmp(reg);
	while(true)
	{
		bool found = false;
		for(int i=0;i<tmp.size();i++)
		{
			node now = tmp[i];
			int ta = now.a;
			int tb = now.b;
			if(ta-x<=k && tb+x>=k)
			{
				found = true;
				int len = tb-ta;
				if(ta+x>=k) k += len;
				else k = tb+x;
				tmp.erase(tmp.begin()+i);
				break;
			}
		}
		if(!found || k>=maxn) break;
	}
	return k>=maxn;
}

对于每一个x(也就是mid),可知所有的区间移动距离都小于等于x,那么对于一个区间[a,b],其移动范围也就是[a-x,b+x]之间。
然后我们的k从0开始,在区间内找其覆盖,如果k在[a-x,b+x]之间,那么这个区间就可以把k覆盖,然后我们进行如下讨论:

	int len = tb-ta;
	if(ta+x>=k) k += len;
	else k = tb+x;

ta是目前的区间的左端点,tb是目前区间的右端点。

如果ta+x>=k,那么我们只需要让区间的左端点移动到k处,右端点此时在k+len处。这样的变换的效果是最好的。(既减少了重复覆盖,移动距离也在x之内)

如果ta+x<k,那么我们最多只能移动x个距离,所以现在的k变成x+tb

如果我们找不到可以覆盖的区间,或者k已经达到maxn了,那么我们就可以认为这一轮的check()结束了。

选择了一个区间之后,把它删除,避免以后再次用到。

仔细观察样例我们发现,答案存在0.5,那么我们将所有东西扩大两倍,最后再除2就可以了。

下面是完整代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 20000;
int n;
struct node
{
	int a;
	int b;
};
vector<node> reg;
bool cmp(node x,node y)
{
	return x.b<y.b;
}
bool check(int x)
{
	int k = 0;
	vector<node> tmp(reg);
	while(true)
	{
		bool found = false;
		for(int i=0;i<tmp.size();i++)
		{
			node now = tmp[i];
			int ta = now.a;
			int tb = now.b;
			if(ta-x<=k && tb+x>=k)
			{
				found = true;
				int len = tb-ta;
				if(ta+x>=k) k += len;
				else k = tb+x;
				tmp.erase(tmp.begin()+i);
				break;
			}
		}
		if(!found || k>=maxn) break;
	}
	return k>=maxn;
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		a *= 2;
		b *= 2;
		reg.push_back({a,b});
	}
	sort(reg.begin(),reg.end(),cmp);
	int l = 0,r = maxn;
	double ans = 0;
	while(l<=r)
	{
		int mid = (l+r)/2;
		if(check(mid))
		{
			r = mid-1;
			ans = mid;
		}
		else
		{
			l = mid+1;
		}
	}
	ans/=2.0;
	cout<<ans<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/87922292