2018年清华软院保研夏令营第2题:上凸函数(二分查找)

题目

在一个上凸函数上均匀采样得到{f(i)|i=1,2…N},有M次询问,每次询问输入两个整数a,b,求{f(i)-a*i-b|i=1,2…N}的最大值。 
输入:第一行,N和M,其中N,M<5e5
然后N行,按顺序输入f(i) 
然后M行,每行两个整数 a,b

--------------------------------------------------------------------------------

思路

运用性质:f[i]-a*i-b最大值处的f函数的切线斜率等于a
二分查找f'[i] = diff[i](离散差分数组)中最小的大于等于a的数的坐标i(可能出现连续多个diff[i]相等的情况,取边缘)
f[i]-a*i-b取最小值的坐标在i的+-1的距离内
注意1:i在diff数组两头的情况
注意2:C++中f[i]的下标从0开始,但题目中f(i)下标从1开始

--------------------------------------------------------------------------------

代码

主程序

// 运用性质:f[i]-a*i-b最大值处的f函数的切线斜率等于a
// 二分查找f'[i] = diff[i](离散差分数组)中最小的大于等于a的数的坐标i
// f[i]-a*i-b取最小值的坐标在i的+-1的距离内
// 注意1:i在diff数组两头的情况
// 注意2:C++中f[i]的下标从0开始,但题目中f(i)下标从1开始

#include<cstdio>

const int NMAX = 5e5+5, INF = 0x3f3f3f3f;
int n, m;
int f[NMAX] = {}, diff[NMAX] = {};	// diff: f的差分,长度为n-1, 由于f是上凸函数,diff是递减函数

int binary_search(int a)			// 在diff[i]中搜索最小的大于等于a的数的坐标
{
	int l = 0, r = n-2, m = (l+r)/2;
	if (diff[0] < a)
	{
		return 0;
	}
	else if (diff[n-2] > a)
	{
		return n-2;
	}
	while (1)
	{
		if (l == r)
		{
			return l;
		}
		else if (r - l == 1 && diff[r] < a && diff[l] >= a)
		{
			return r;
		}
		m = (l+r)/2;
		if (diff[m] >= a)
		{
			l = m;
		}
		else if (diff[m] < a)
		{
			r = m;
		}
	}
}


int main()
{
#ifndef ONLINE_JUDGE
	freopen("xlySE18_02.txt", "r", stdin);
#endif
	int i = 0, a, b, ans1, ans2, ans3;
	scanf("%d%d", &n, &m);
	for (i=0; i<n; i++)
	{
		scanf("%d", f+i);
		if (i > 0)
		{
			diff[i-1] = f[i] - f[i-1];		// 离散差分近似切线斜率
		}
	}
	while (m--)
	{
		scanf("%d%d", &a, &b);
		i = binary_search(a);
		if (i == 0)
		{
			ans1 = f[0] - a - b;
			ans2 = f[1] - a*2 - b;
			ans3 = f[2] - a*3 - b;
		}
		else if (i == n-2)
		{
			ans1 = f[n-1] - a*(n-1+1) - b;
			ans2 = f[n-2] - a*(n-2+1) - b;
			ans3 = f[n-3] - a*(n-3+1) - b;
		}
		else
		{
			ans1 = f[i] - a*(i+1) - b;
			ans2 = f[i-1] - a*(i-1+1) - b;
			ans3 = f[i+1] - a*(i+1+1) - b;
		}
		ans1 = ans1 > ans2 ? ans1 : ans2;
		ans1 = ans1 > ans3 ?  ans1 : ans3;
		printf("%d\n", ans1);
	}
	return 0;
}

对拍程序

// xlySE18_02.cpp的对拍程序,暴力法

#include<cstdio>
#include<algorithm>

const int NMAX = 5e5+5, INF = 0x3f3f3f3f;
int n, m;
int f[NMAX] = {};	

int main()
{
#ifndef ONLINE_JUDGE
	freopen("xlySE18_02.txt", "r", stdin);
#endif
	int i = 0, a, b, ans = -INF;
	scanf("%d%d", &n, &m);
	for (i=0; i<n; i++)
	{
		scanf("%d", f+i);
	}
	while (m--)
	{
		scanf("%d%d", &a, &b);
		ans = -INF;
		for (i=0; i<n; i++)
		{
			ans = std::max(ans, f[i]-a*(i+1)-b);
		}
		printf("%d\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/82460671