选数排列

选数排列 ⁡ \operatorname{选数排列}

题目链接: luogu T145305 ⁡ \operatorname{luogu\ T145305} luogu T145305 / SSL比赛 1517 ⁡ \operatorname{SSL比赛\ 1517} SSL 1517

题目

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

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

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

输入

第一行给出 3 3 3 个整数 N , R , C N,R,C N,R,C ,对应题目中描述的参数。

接下来一行有 N N N 个整数,代表 N N N 个可以选择的数 P i P_i Pi

输出

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

样例输入

7 2 3
170 205 225 190 260 225 160

样例输出

30

数据范围

对于 50 % 50\% 50% 的数据, 1 ≤ N ≤ 1000 1\le N\le 1000 1N1000

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

思路

这道题是一道二分加贪心,但是也有人用二分加 dp 。

我们可以发现,我们要选几个数让最大值减最小值尽可能少,那就肯定要让这些数相差小,可以想到把数从小到大排序。
然后我们就可以求出选第 i i i 个数到第 i + c i+c i+c 个数所得出的那个值 D D D

那我们就可以用二分来做。
就二分最后输出的答案,那很明显就要让上面求出的 D D D 值大于二分的答案而且数量要大于等于 R R R
那怎么保证选的最优呢?肯定就是前面的能选就选啊!尽可能选最前面而且可以选的就好了。
不过因为一个数用了就没了,那你选完第 i i i 组之后,只能从 i + c i+c i+c 组开始挑。

这样做就可以了。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

int n, R, c, a[500001], l, r, mid, ans;
int cha[500001];

bool ch(int now) {
    
    //看能不能摆出R组数
	int num = 0;
	for (int i = 1; i <= n - c + 1; i++)
		if (cha[i] <= now) {
    
    
			num++;//记录已经选出多少组数了
			if (num >= R) return 1;
			i = i + c - 1;//不能用重复的数
		}
	return 0;
}

int main() {
    
    
	scanf("%d %d %d", &n, &R, &c);//读入
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);//读入
		r = max(r, a[i]);//记录范围
	}
	
	sort(a + 1, a + n + 1);//按大小排序
	for (int i = c; i <= n; i++)
		cha[i - c + 1] = a[i] - a[i - c + 1];//求出每c个之间的差
	
	while (l <= r) {
    
    //二分出答案
		int mid = (l + r) / 2;
		if (ch(mid)) {
    
    
			r = mid - 1;
			ans = mid;
		}
		else l = mid + 1;
	}
	
	printf("%d", ans);//输出
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108209631