2023.8.31

#A. 没出现的数字

  • 传统题1000ms256MiB

Description

给你一个数字N,及Q

然后给你N个数字,Q代表有多少个询问

每个询问会给你一个数字x

询问你在正整数集合中,除开给你的这N个数字之外

第X个数字是多少。

Format

Input

第一行给出N,Q

第二行给出N个数字,权值<=1e18

接下来Q行,每行一个数字x

1<=N,Q<=1e5

x<=1e18

Output

如题目

Samples

输入数据 1

4 3
3 5 6 7
2
5
3

输出数据 1

2
9
4

二分枚举加在哪个读入数字的后面

统计出各个读入数字之前可以累计加多少个数字 

#include<bits/stdc++.h>
using namespace std;
int n,q,po;
long long d[100002],sur[100002],x;//sur[i]:第i+1个数字前可加的数字 
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
		scanf("%lld",&d[i]);
	sort(d+1,d+1+n);
	sur[0]=d[1]-1;
	for(int i=1;i<=n;i++)
		sur[i]=sur[i-1]+d[i+1]-d[i]-1;
	sur[n]=LLONG_MAX;
	for(int i=1;i<=q;i++)
	{
		scanf("%lld",&x);
		po=lower_bound(sur,sur+1+n,x)-sur;//大于或等于 
		printf("%lld\n",d[po]+x-sur[po-1]);
	}
	return 0;
}

#B. 数列分段II

  • 传统题1000ms256MiB

Description

对于给定的一个长度为 N 的正整数数列 A ,现要将其分成M 段,并要求每段连续,且每段和的最大值最小。

例如,将数列4 2 4 5 1 要分成 3 段:

若分为(4,2),(4,5),(1) ,各段的和分别为6,9,1 ,和的最大值为9 ;

若分为4,(2,4),(5,1) ,各段的和分别为4,6,6 ,和的最大值为 6;

并且无论如何分段,最大值不会小于6 。

所以可以得到要将数列4 2 4 5 1 要分成3 段,每段和的最大值最小为6 。

Format

Input

第1 行包含两个正整数N,M;

第2 行包含N 个空格隔开的非负整数Ai ,含义如题目所述。

N<=1e5,M<=N

Ai之和可能超过1e9

Output

仅包含一个正整数,即每段和最大值最小为多少。

Samples

输入数据 1

5 3
4 2 4 5 1

输出数据 1

6

二分枚举答案

最大值与份数成负相关,份数>m-->最大值偏小;份数<m-->最大值偏大

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long d[100002],l,r,mid,ans=LLONG_MAX;
bool check(long long mid)//错过 
{
	int pt=1;
	long long sum=0;
	for(int i=1;i<=n;i++)
		if(sum+d[i]<=mid)
			sum+=d[i];
		else
		{
			sum=d[i];
			pt++;
		}
	if(pt<=m)
		return true;
	return false;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&d[i]);
		l=max(l,d[i]);//最小的最大值 
		r+=d[i];//最大的最大值 
	}	
	while(l<=r)
	{
		mid=(l+r)/2;
		if(check(mid)==true)
		{
			ans=min(ans,mid);
			r=mid-1;
		}
		else
			l=mid+1;
	}
	printf("%lld",ans);
	return 0;
}

坑点:long long和int,容易错 

可以define int long long,虽然会占内存,但方便查错

#C. 砍树

  • 传统题1000ms256MiB

Description

在一片树林里, 有 N 颗树,你需要砍下 M 米的木头.

仅有一排树木可以供你砍伐

伐木机的工作原理如下:

设置高度参数 H(以米为单位)

机器将一个巨大的锯片提升到那个高度,并切断高于 H 的所有树木部分

(当然,不高于 H 米的树木仍然完好无损).

例如:

如果有一排树木高度为 20,15,10 和 17 米,参数设为 15 米,

切割后剩余的树的 高度将是 15,15,10 和 15 米,得到的木头为第一棵树的 5 米和第四棵树的 2 米,总共 7 米。

因为提倡生态意识,所以不能砍下不必要的木头。

这就是为什么要把锯条尽可能地抬高。

请你找到最大的整数高度的锯片,使得你砍下至少 M 米的木头.

Format

Input

第一行包含两个空格分隔的正整数N,M ,含 义见题面.

第二行输入包含 N 个空格分隔的小于 1000000 的正整数.

第 i 个正整数表示第 i 棵的 高度.

保证所有树的高度和大于 M.

1≤N≤1 000 000

1≤M≤2 000 000 000

Output

一行一个正整数 H,即可以获得 M 米木材的前提下,设置锯片的最大高度

Samples

输入数据 1

4 7
20 15 10 17

输出数据 1

15

输入数据 2

3 7
10 8 5

输出数据 2

5

输入数据 3

3 6
10 8 5

输出数据 3

6

法一:二分+前缀和优化

#include<bits/stdc++.h>
using namespace std;
int n,m,d[1000002],ans,l,r,mid;
long long sur[1000002],tot,num,p;
signed main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&d[i]);
		r=max(r,d[i]);
		tot+=d[i];
	}	
	sort(d+1,d+1+n);
	for(int i=1;i<=n;i++)
		sur[i]=sur[i-1]+d[i];
	while(l<=r)
	{
		mid=(l+r)/2;
		p=upper_bound(d+1,d+1+n,mid)-d-1;//upperbound找 大于 mid的第一个值 
		num=sur[p]+mid*(n-p);//1-p不被砍,加原长;p+1-n砍至mid 
		if(tot-num>=m)
		{
			ans=max(ans,mid);
			l=mid+1;
		}	
		else
			r=mid-1;			
	}	
	printf("%d",ans);
	return 0;	
}

法二:贪心

每次让当前的树与前一棵树齐平,直到砍去总和大于等于m,再把多砍的均分给砍过的树

#include<bits/stdc++.h>
using namespace std;
int n,m,d[1000002],tot,ans;
bool cmp(int a,int b)
{
	return b<a;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&d[i]);
	sort(d+1,d+1+n,cmp);
	for(int i=1;i<=n;i++)
	{
		tot+=i*(d[i]-d[i+1]);
		if(tot>=m)
		{
			ans=d[i+1]+(tot-m)/i;
			break;
		}
	}
	printf("%d",ans);
	return 0;	
}

#D. 读书是最幸福的事

  • 传统题1000ms256MiB

Description

学过编程都知道,程序=算法+数据结构

小J有两个书柜,一个全是算法书,另一个全是数据结构的书

小J在从这两个书柜中选书的时候,每个书柜都是总是从第一本书开始到某本书结束

现在小J有K个时间,现在他希望读尽可能多的书

Format

Input

第一行给N,M,K,分别代表两个书柜的书的数目及时间数

第二行给出N个数字,代表算法书的时间,其值<=1e9

第三行给出M个数字,代表数据结构书的时间,其值<=1e9

N,M<=2e5

K<=1e9

Output

如题

Samples

输入数据 1

3 4 240
60 90 120
80 150 80 150

输出数据 1

3

Hint

可以从第1个书柜中选前2本书

再从从第2个书柜中选前1本书

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,sp,j,ans;
long long d1[200002],d2[200002],x,k;
bool ok=false;
signed main()
{
	scanf("%d%d%lld",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		d1[i]=x+d1[i-1];
		if(d1[i]>k&&ok==false)
		{
			sp=i;
			ok=true;
		}	
	}	
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&x);
		d2[i]=x+d2[i-1];
	}	
	if(sp==0)
		sp=n+1;
	for(int i=0;i<=sp-1;i++)
	{
		j=upper_bound(d2+1,d2+1+m,k-d1[i])-d2-1;//upperbound找第一个大于k-d1[i]的,-1后小于或等于k-d1[i]
		if(d1[i]+d2[j]<=k)
			ans=max(ans,i+j);
	}	
	printf("%d",ans);
	return 0;
}
总结一下upper or lowerbound——在升序数组中——
lower_bound()查找的是大于等于输入数字的最靠左的位置
upper_bound()查找的是严格大于输入数字的最靠左的位置
lower_bound()-1查找的是严格小于输入数字的最靠右的位置
upper_bound()-1查找的是小于等于输入数字的最靠右的位置
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值