【2020.8.24 NOIP模拟赛】选数排列【二分】

102 篇文章 1 订阅
18 篇文章 0 订阅

题目描述
给出 N 个数,我们需要选择其中的 R × C R×C R×C个数,,把它们填入一个 R × C R×C R×C 的矩阵(R 行 C 列)中。

我们先定义一个函数 D ( i ) D(i) D(i) 代表第 i 行中最大的数和最小的数之差。对于整个矩阵,定义 F 为矩阵中 D ( i ) ( 1 ≤ i ≤ R ) D(i)(1≤i≤R) D(i)(1iR) 的最大值。

我们需要 F 的值最少,你能求出最少可能达到的 FF 值是多少吗?


输入格式

第一行给出 3 个整数 N , R , C N,R,C N,R,C,对应题目中描述的参数。
接下来一行有 N 个整数,代表 N 个可以选择的数 P i P_i Pi

输出格式

输出一行表示最少可能达到的 F 值。


输入输出样例
输入 #1复制
7 2 3
170 205 225 190 260 225 160
输出 #1复制
30


说明/提示
对于 50% 的数据,1≤N≤1000

对于所有数据, 1 ≤ R , C ≤ 1 0 4 , 1≤R,C≤10^4 , 1R,C104, R × C ≤ N ≤ 5 ∗ 1 0 5 , 0 < P i ≤ 1 0 9 R×C≤N≤5∗10^5,0<P i≤10^9 R×CN5105,0<Pi109


解题思路
这道题我们可以用二分来做。。

因为二分具有单调性,所以我们一开始要将输入的 a [ i ] a[i] a[i]排序,接着我们二分答案——最少可能达到的值。
当 mid 是最少可能达到的值时,可以选出大于等于 r r r个长度为 c c c的序列时, l = m i d − 1 l=mid-1 l=mid1。否则 r = m i d + 1 r=mid+1 r=mid+1


代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
long long a[600000],h[600000],mid,ll,rr,ans,n,r,c;//h[i]表示选到第i个数时,已经选出的了几层
long long read()
{
	register long long X=0;register bool flag=1; register char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
bool check(long long f)
{
	memset(h,0,sizeof(h));
	for(long long i=c;i<=n;i++)
	{
		h[i]=h[i-1];
		if(a[i]-a[i-c+1]<=f)	
			h[i]=h[i-c]+1;//又找到一个就累加
	}
	if(h[n]>=r) return 1;
	else return 0;
}
int main(){
	n=read();
	r=read();
	c=read();
	for(register long long i=1;i<=n;i++)
		a[i]=read();
	sort(a+1,a+n+1);
	long long ll=0,rr=a[n]-a[1];
	while(ll<rr)//二分答案
	{
		mid=(ll+rr)/2;
		if(check(mid))
			rr=mid;
		else ll=mid+1;			
	}
	printf("%lld",ll);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值