洛谷 P2564 [SCOI2009]生日礼物

题目链接

题目大意

有n个礼物,m种类型,每个礼物都有一个一维坐标,现在你要从中挑选出m个类型互不相同的礼物,使得m个礼物之间的坐标差的最大值最小化。

思路

首先,每个礼物有两个信息,一个是类型,一个是坐标。那么我们可以用一个结构体来存这个礼物信息。然后我们来看一个有趣的 方法,每个礼物不是有一个坐标么,那我们可以把他们先按从小到大的顺序摆在x轴上,然后我们用一个卷尺去量,坐标最小的礼物为第一个礼物,然后往后量,一直量到第一次包括所有类型的礼物。
例如:
假设一共有三种类型的礼物
在这里插入图片描述
那第一次量的结果就是5 - 1 = 4。

那我们后面的操作就是每次加一个礼物进来,然后慢慢删掉之前多余的礼物,使得区间最小。
例如:
在这里插入图片描述
这就是第一次到第三次量的结果,单拿出第二次来说。

相较于第一次,我们加入了第五个礼物,他的类型为1,坐标为7。

然后我们缩短前面的区间,现在已经有两个类型为1的礼物了,所以可以把第一个礼物删掉,然后碰到第二个礼物的时候发现也已经有两个礼物了,那我们也可以把第二个礼物删掉。

移动到第三个礼物时,发现类型为2的礼物只剩一个了,所以以第五个礼物为终点的区间的起点就是第三个礼物了。

后面就以此类推,我们只需要更新每一次右区间增加以后的左区间就好了。

上代码!!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
#define pi acos(-1)
#define inf 0x3f3f3f3f
#define pii pair<int, int>
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define piii pair<pii, int>
#define uf(a, b, i) for (register int i = (a); i <= (b); ++i)
#define df(a, b, i) for (register int i = (a); i >= (b); --i)
using namespace std;
inline int read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}
template<class T>
inline void print(T x) {
	if(x > 9) print(x/10);
	putchar(x%10 + '0');
}
template<class T>
T Max(T a, T b) {
  return a > b ? a : b;
}
template<class T>
T Min(T a, T b) {
  return a < b ? a : b;
}
const ll mod = 1e9 + 7;
int n, m, num;
int vis[66];
struct point {
  int mode, pos;
  bool operator < (const point &pp) {
    return pos < pp.pos;
  }
} p[1000006];
void scan() {
  n = read(); m = read();
  uf (1, m, i) {
    int cnt = read();
    uf (1, cnt, j) {
      p[++num].mode = i;
      p[num].pos = read();
    }
  }
}
void work() {
  sort(p+1, p+num+1);
  int ans, l = 1, r = 1, number = 0;
  while (1) {
    if (!vis[p[r].mode]) number++;
    vis[p[r].mode]++;
    if (number == m) break;
    r++;
  }
  ans = p[r].pos - p[l].pos;
  uf (r+1, n, i) {
    vis[p[i].mode]++;
    while (vis[p[l].mode] > 1) {
      vis[p[l].mode]--;
      l++;
    }
    ans = Min(ans, p[i].pos - p[l].pos);
  }
  print(ans);
  putchar('\n');
}
int main() {
	scan();
	work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值