P2652 同花顺
题目背景
所谓同花顺,就是指一些扑克牌,它们花色相同,并且数字连续。
题目描述
现在我手里有 (n) 张扑克牌,但它们可能并不能凑成同花顺。我现在想知道,最少更换其中的多少张牌,我能让这 (n) 张牌都凑成同花顺?
输入格式
第一行一个整数 (n) ,表示扑克牌的张数。
接下来 (n) 行,每行两个整数 (a_{i}) 和 (b_{i}) . 其中 (a_{i}) 表示第 (i) 张牌的花色,(b_{i}) 表示第 (i) 张牌的数字。
输出格式
一行一个整数,表示最少更换多少张牌可以达到目标。
输入输出样例
输入 #1
5
1 1
1 2
1 3
1 4
1 5
输出 #1
0
输入 #2
5
1 9
1 10
2 11
2 12
2 13
输出 #2
2
说明/提示
-
对于 30% 的数据,(n le 10);
-
对于 60% 的数据,(n le 10^{5}),(1 le a_{i} le 10^{5}),(1 le b_{i} le n);
-
对于 100% 的数据,(n le 10^{5}),(1 le a_{i}, b_{i} le 10^{9});
前言:
这道题好ex,考试考了好几次了都没写出来(怪自己太菜了)。
题解
首先呢,这道题有一个比较特殊的性质,就是重复的牌一定会被我们替换掉,这就以意味着我们需要对牌进行去重。
我们可以转化一下问题,求一下最长的符合同花顺序列的长度,这样就比较好求了。
我们最后的答案就是 原来的牌数减去最长的符合同花顺序列的长度。
假设,我们以这张牌作为当前同花顺序列的最后一张牌,那么前面的那几张牌的数字都是要 大于当前的牌的数字减 (n)
所以我们可以对牌按花色排序,如果花色相同按牌的数字排序。
我们开个队列存当前的同花顺序列,如果当前的牌 (i) 与之前的牌的花色都不同,就把整个队列清空。
反之,枚举左端点,找到第一个与他花色不同或者数字比他小 (n) 的牌 (j),同花顺序列的长度就是 (i-j)
可以考虑用一个队列来优化一下:
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1e5+10;
int n,cnt,ans,tot;
queue<int> que;
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
struct node
{
int c,num;
}q[N],a[N];
bool comp(node a,node b)//按花色排序,花色相同按数字排序
{
if(a.c == b.c) return a.num < b.num;
else return a.c < b.c;
}
int main()
{
// freopen("card.in","r",stdin);
// freopen("card.out","w",stdout);
n = read();
for(int i = 1; i <= n; i++)
{
q[i].c = read();
q[i].num = read();
}
sort(q+1,q+n+1,comp);
for(int i = 1; i <= n; i++)//去重
{
if(q[i].c == q[i-1].c && q[i].num == q[i-1].num) continue;
else a[++tot] = q[i];
}
for(int i = 1; i <= tot; i++)
{
if(a[i].c != a[i-1].c)//与之前花色不同,把队列清空
{
while(!que.empty()) que.pop();
}
while(que.size() && a[que.front()].num < a[i].num - n) que.pop();//去掉数字比他小n的点
que.push(i);
ans = max(ans,(int) que.size()); //更新一下最长同花顺序列的长度
}
printf("%d
",n - ans);
fclose(stdin); fclose(stdout);
return 0;
}