本题采用了尺取法,具体思路如下:
- 设置一个结构体存放每个珠子的种类和位置,在读入珠子的数据后,将珠子按照位置的前后进行排序
- 设置两个
指针
left和right来表示送出去彩带的两个端点
- left不动,right向后遍历,每次遍历到的珠子类型存放到kind数组中,kind[i]表示第i类珠子的个数,kind_count表示已经存入珠子的种类个数
- 当kind_count == k,即选取的这一段包含所有种类的珠子,更新送出去珠子的长度min_len;left向右遍历,直到kind_count != k,注意:left遍历的时候要将之前left所指的珠子的从kind中拿出去(left到right这一段才是要送出去的珠子,不在这一段的要拿出去)
#include <iostream>
using namespace std;
#include <stdio.h>
#include <algorithm>
#define ll long long
int kind[100]; //存放选取的种类
int kind_count = 0; //存放选取种类的个数
struct Node
{
ll pos; //珠子所在的位置
ll type; //珠子的类型
} node[1000100];
//珠子的排序函数
bool cmp(struct Node a, struct Node b)
{
if(a.pos == b.pos) return a.type < b.type;
return a.pos < b.pos;
}
int main(void)
{
ll n, k;
scanf("%lld%lld", &n, &k);
ll j = 1;
for(ll i = 1; i <= k; i++)
{
ll t; //代表第i种珠子的个数
scanf("%lld", &t);
while(t--)
{
scanf("%lld", &node[j].pos);
node[j].type = i;
j++;
}
}
//对珠子按位置升序
sort(node+1, node+n+1, cmp);
ll left = 1,right = 1; //彩带的左右节点
ll min_len = 1e9; //记录彩带的最短距离
for(; right <= n; right++)
{
//将珠子种类放入kind中,如果之前没有这个种类,则种类个数kind_count减一
kind[node[right].type]++;
if(1 == kind[node[right].type])
kind_count++;
while(kind_count == k)
{
min_len = min(min_len, node[right].pos - node[left].pos);
//left右移,将left之前所指的珠子取出,如果取出后这个种类的珠子数目为0,则kind_count--
kind[node[left].type]--;
if(0 == kind[node[left].type])
kind_count--;
left++;
}
}
printf("%lld\n", min_len);
return 0;
}