2020暑期多校D9-F 思路+证明
主为自用,欢迎指正。
https://ac.nowcoder.com/acm/contest/5674/F
前置知识:
尺取法:一种反复推进区间的头尾,求出满足条件的最小区间的技巧。
题意:
从n天中选m天,要让m天所穿的衣服价值的最大值与最小值差值最小。
思路:
由于每天有不定件衣服可选取,但是最多只有2e6件衣服,一维数组完全存得下,故想到排序,暴力每段长度为m的区间,若天数少了则向右拓展,维护最大值与最小值之间的差值。时间复杂度为O(n*n)。
后来直接TLE,故决定用尺取降低时间复杂度,尺取的核心在于对满足与不满足条件时的左右区间端点进行修改,故O(n)时间复杂度即可。
代码步骤:
将所有衣服按照价值升序存至一维数组,随后尺取,详细可见代码注释。
AC代码:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> #include<map> #include<math.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int N = 2e6 + 10; int Case; int n, m; int num; int min1, max1 = 0; int count1 = 0; int ha[N]; struct point1 { int v; int l; }p[N]; bool cmp1(point1 a, point1 b) { return a.v < b.v; } void solve() { int res = INF; int cou = 0; //区间内不同天数 int s = 1; for (int t = 1; t <= count1; t++) { if (!ha[p[t].l]) cou++; ha[p[t].l]++; //选择该天的衣服件数 while (cou == m) { res = min(res, p[t].v - p[s].v); //选取最小差值 ha[p[s].l]--; //则第p[s].l天的衣服可去掉 if (!ha[p[s].l])//若去掉第s小件衣服后,改天p[s].l不再被选取则选取不同天数减一 cou--; s++; //每次cou==m,必定使原点前进 } } printf("%d\n", res); } int main() { scanf("%d%d", &n, &m); memset(p, 0, sizeof(p)); memset(ha, 0, sizeof(ha)); for (int i = 1; i <= n; i++) { scanf("%d", &num); for (int j = 1; j <= num; j++) { scanf("%d", &p[++count1].v); p[count1].l = i; } } sort(p + 1, p + count1 + 1, cmp1); solve(); // for (int i = 0; i < count1; i++) // printf("%d", p[i].v); }
简单图证:
注:
数据给的很差,默认最小价值差为,最小价值衣服为端点至m件不同衣服的最大值的查值。