test Week3

   3/5 keep fighting!!  B题的二分答案的出口要搞清。。


A题:

按非降序给出N(2<=N<=15000)个范围在[1, 1e9]的整数,组成集合A,询问M(1<=M<=1e6)次,每次给出一个数,问该数属于集合A的次数。

 已知给出的N个数为非降序,在M次查询时,可以使用二分法进行查询,二分法可以用STL中的set实现。

B题:

给定N天每天花的钱数,连续的一段日子可以组成一个月,问将N天组成M个月时,每个月的最少花费是多少。

这是一个最小值最大化的问题,由于问题的答案落在一个区间内:左界是N天里的单天花费的最大值,右界是N天的花费总和,在检验了区间里的某个值的正确性后,就可以确定该点到右界的区间内没有最优解,满足二分答案的思路。 

检验某一值x的正确性时,可以采用贪心的策略,当连续几天的花费大于X时,counter++,最后如果counter大于M,即可以x为最大花费时分组可以分成M个月或以上,则x为可行解,否则为不可行解。要关注二分的输出结果情况

#include <iostream>
#include <cstdio>
using namespace std;

int N, M;
const int MAXN = 100000 + 5;
int m[MAXN];
long long L, R, mid;

bool test(int value)
{
	int counter = 1;
	long long sum = 0;
	for (int i = 0; i < N; i++) {
		if (sum + m[i] > value) {
			sum = m[i];
			counter++;
		}
		else sum += m[i];
	}
	if (counter > M) return true;
	return false;
}

int main() 
{
	cin >> N >> M;
	L = 0;
	long long sum = 0;
	for (int i = 0; i < N; i++) {
		scanf("%d", &m[i]);
		if (L < m[i]) L = m[i];
		sum += m[i];
	}
	R = sum;
	while (L <= R) {
		mid = (L + R) / 2;
		if (test(mid)) L = mid + 1;
		else R = mid - 1;
	}
	cout << mid << endl;
}	

C题

有一本高为H的书,一个长L,两端高为h的书架,书直立在书架的左端,人从书的底端开始拉书,书开始缓缓倒入书架里,问这个过程中,书的重心偏离书架左端的最远距离


HL的夹角为θ,由相似三角形关系得:重心到左侧的d值为

d = cos(θ)*H/2 - h/tan(θ);

由题意可得该过程的初始状态是书紧贴左侧(θ= PI),此时的d值为0, 结束状态如上图所示:当重心H/2恰好在h之上时,此时的d值也为0,此后重心在h的右侧,d值为负数不用考虑。那么在θ=PI到arcsin(2*h/H)的过程中,是一个先增后减的过程,可以用三分法找到峰值。


 D题

人开车沿x轴从左向右行使,给定两个点的坐标(x1, y1), (x2, y2) [-1000, 1000] , x轴上的点与这两点能够组成的最大角的弧度数。

     情况一: 两个坐标点Y值相同,组成的线段与x轴平行,此时线段的延长线与x轴无交点,那么在x[-1e4, 1e4]过程中,角ABC是一个增加后减小的过程,那么对[-1e4, 1e4]过程进行三分即可得到极大值

情况二:两个坐标点Y值不同但同号,此时线段的延长线与x轴有交点(X0, 0),当B点在(X0, 0)处时,易得角ABC=0 因此X[-1e4, X0] ,  [X0, 1e4]这两个过程中角ABC是一个增加后减小的过程, 用三分法求[-1e4, X0] ,  [X0, 1e4]这两个区间的极大值后,较大者为结果

情况三:两坐标点Y值异号时,即线段穿过x轴,当B点在(X0, 0)处时,易得角ABC大小为PI

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

#define PI acos(-1)
#define eps 1e-8
#define LBOUND -1e4
#define RBOUND 1e4

double X1, X2, Y1, Y2;
double X0, answer;

double calcu(double x) 
{
	double a = x*x - x*X1 - x*X2 + X1*X2 + Y1*Y2; 
	double b = sqrt(Y1*Y1 + pow((x-X1), 2));
	double c = sqrt(Y2*Y2 + pow((x-X2), 2));
	return acos(a / (b * c));
}

void solve(double L, double R) 
{
	double Lmid, Rmid;
	while (L + eps < R) {
		Lmid = (L * 2 + R) / 3;
		Rmid = (L + R * 2) / 3;
		if (calcu(Lmid) >= calcu(Rmid)) R = Rmid;
		else L = Lmid;
	}
	if (calcu(L) > answer) answer = calcu(L);
}

int main()
{
	cin >> X1 >> Y1 >> X2 >> Y2;
	if (Y1 != Y2) {
		X0 = X1 - Y1*((X1-X2+0.0) / (Y1-Y2));
		if (Y1 * Y2 <= 0) answer = PI;
		solve(LBOUND, X0);
		solve(X0,RBOUND);
	}
	else solve(LBOUND, RBOUND); 
	printf("%.6lf\n", answer);	
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值